为什么要“除夕”,原来是内存爆了!

存储 存储软件
传说古代有一只四角四足的怪兽:名叫夕。因冬天大雪导致夕没东西吃,所以夕经常到附近的村里找吃的,因其身体庞大、脾气暴躁、凶猛异常,给村民带来了很大的灾难。

[[381658]]

传说古代有一只四角四足的怪兽:名叫夕。因冬天大雪导致夕没东西吃,所以夕经常到附近的村里找吃的,因其身体庞大、脾气暴躁、凶猛异常,给村民带来了很大的灾难。

后来有一位聪明的孩子,他叫做年,教给大家除掉“夕”的方法:用爆竹,轻则赶走它,重则伤它。每年腊月三十,夕都会来村里,村名就守着夜,放着鞭炮赶走夕。除夕由此而来。

”我们把“夕”想象成一个不断吃机器内存的 Java 程序,就称它为 年兽吧。掌管 Java 虚拟机内存的就是“年”,我们称它为年哥吧。

年兽的地盘

年哥管理的地盘主要分为五大区:堆、方法区、虚拟机栈、本地方法栈、程序计数器。如下图所示。另外大家可以把图中的线程想象成村民,而堆是作为村民共享使用的区域。

运行时数据区

 

堆又可以进行细分,分为新生代和老年代,新生代和老年代的比例是 1:2,而新生代又可以进行细分,分为伊甸园(Eden)区和两个 Survivor, 其中 Eden 区大小和Survivor 区大小是 8:1。

如下图所示,年兽和村民都是共享堆内存这块地盘的,管理员年哥是管理堆内存的。其中的数字 1、8、20 分别代表占用内存的份数。

共享堆区

 

年兽的胃口

年兽的胃口是村民的几百倍,年兽假扮村民逃过了管理员年哥的检查,年哥对于这种大胃王都是直接分配到老年代去的,因为大胃王需要连续的内存给它吃,而新生代的碎片比较多不满足条件。在 Java 的世界中,最典型的大胃王就是大对象:如很长的字符串,或者元素数量很庞大的数组。

如下图所示,村民分配到新生代吃内存,年兽被直接分配到老年代。

年兽被直接分配到老年代

 

大量年兽入侵

年兽尝到甜头后,就开始不断地呼叫它的亲戚朋友,大量年兽被分配到了老年代,直接导致老年代的内存空间不足了,如下图所示:

大量年兽入侵

 

代码演示

我们用代码来演示下年兽入侵:

  • 创建了 3 个年兽,都占用 10 MB 内存。
  1. public class SpringFestivalOOM { 
  2.     public static void main(String[] args) { 
  3.         // 年兽1/2/3,都占用 10 MB 内存 
  4.         byte[] nianShou1 = new byte[10 * 1024 * 1024]; 
  5.         byte[] nianShou2 = new byte[10 * 1024 * 1024]; 
  6.         byte[] nianShou3 = new byte[10 * 1024 * 1024]; 
  7.     } 
  • 编译这段程序。
  1. javac SpringFestivalOOM.java 
  • 执行这段程序,同时设置堆内存最大为 20 MB。
  1. java -Xms20M -Xmx20M SpringFestivalOOM 

因为 3 个年兽占用的内存 30 MB 大于堆的最大内存 20 MB,所以抛出堆内存溢出异常,如下图所示:

堆内存溢出异常

 

这个时候年哥和村民才发现,原来有这么多年兽占了我们的地盘,赶快消灭它们!

打走年兽

村民们和年哥凑到一块,讨论了下该如何解决这个问题,究其原因就是年兽太多了,要减少他们呼朋唤友来吃内存。

放到我们的 Java 世界中,就是减少大对象的频繁创建。

我们程序员经常出现本地写完代码后没什么问题,到线上后就出问题,很可能的原因就是线上环境的数据量大,很容易出现大对象的频繁创建,比如大型促销活动时,短时间内需要创建大量订单数据,而订单数据又比较复杂,有很多字段,可能会占用大量的内存空间,最终导致频繁触发垃圾回收,而垃圾回收时又会出现 Stop the world 现象,应用程序的性能就降下来了。

守岁

在除夕晚上,都会进行“守岁”,村民们齐聚一堂吃着年夜饭,一起等待除夕的钟声。等到天亮再拜访亲戚邻居。

而守岁这个过程只能待在家里,不能做其他事情,所以可以看成是垃圾回收时,其他线程不能工作,也就是 Stop the world 的由来。

如下图所示,除夕之前,村民可以去其他地方活动,除夕夜就只能待在家里守岁了,到了第二天早上就可以串门拜年了。

守岁

 

总结

本篇通过除夕的故事来讲解 Java 中垃圾回收机制,因故事较为简单,所以并没有对垃圾回收算法进行深入讲解,本篇只能算作垃圾回收的入门,希望能给大家带来一定启发作用,对 JVM 很熟的同学就当学习下除夕的来历吧~

  • 村民作为小对象使用堆区的新生代,年兽作为大对象直接使用堆区的老年代。
  • 除夕当晚,大量年兽入侵老年代,导致堆区内存不足,触发垃圾回收机制。
  • 守岁就是待在家里守着过新年,而垃圾回收时,又会停止其他线程,也就是 Stop the world。
  • 避免代码中频繁复制或创建大对象是必须做的事情,以免上线后出现问题。
  • 除夕也代表着辞旧迎新,这不正是执行垃圾回收吗?

本文转载自微信公众号「悟空聊架构」,可以通过以下二维码关注。转载本文请联系悟空聊架构公众号。

 

 

责任编辑:武晓燕 来源: 悟空聊架构
相关推荐

2019-08-30 10:27:37

数据库通信技术

2021-02-02 09:13:11

索引SQL数据库

2021-11-29 18:34:14

内存异步死循环

2019-11-28 14:14:16

微信QQ腾讯

2017-06-06 15:13:07

2022-01-09 23:44:14

5G4G手机

2020-03-23 08:30:12

程序员男友感受

2009-03-10 12:42:45

2017-11-02 21:17:35

数字化

2022-12-14 07:32:40

InnoDBMySQL引擎

2021-02-07 08:13:18

@DateTimeFo@NumberFormSpring

2018-01-31 14:50:04

LinuxmacOS .bashrc

2022-05-05 08:55:12

工业物联网IIoT

2023-05-22 15:58:11

2024-02-06 09:30:25

Figma矩形矩形物理属性

2020-02-23 15:55:00

疫情AI人工智能

2022-07-13 10:37:59

服务器故障优化

2011-05-25 09:58:46

C#

2017-11-12 21:12:34

HPC

2018-04-02 15:13:21

网络
点赞
收藏

51CTO技术栈公众号