JVM之堆内存分配

JVM之堆内存分配

最近在研究JVM堆内存之空间的分配以及GC的使用,将自己的理解写到此处。主要分析下年轻代以及老年代的内存模型以及发生GC时各个区都做了什么。

堆内存的构成

JVM之堆内存分配

由图可知:堆内存分为三个区域:
- 年轻代的构成
JVM之堆内存分配

年轻代分为三个区域:EDEN、Survivor0(简称S0)、Survivor1(简称S1),其中S0与S1的大小是相同等大的,三者所占年轻代的比例大致为8:1:1,S0与S1就像我们说的”孪生兄弟”一样,我们大家不必去纠结此比例(可以通过修改JVM某些动态参数来调整)的大小,只需谨记三点就好:
1.S0与S1相同等大
2.EDEN区远比S(S0+S1)区大。EDEN占了整个年轻代的大致70%至80%左右。
3.年轻代分为2个区(EDEN区、Survivor区)、3个板块(EDEN、S0、S1)
- 老年代的构成
JVM之堆内存分配

老年代就只有一个区域,没有在进行细分,在下面分析GC时会讲到为什么没有进行细分。年轻代的空间一般为老年代的1/3或者1/4大小。
- 永久代的构成
永久代我们目前先不过多叙述。目前只是先对年轻代以及老年代作下分析。

GC

当大家在成程序中使用new关键字或newInstance()等方式创建对象时,系统默认都是先在EDEN区为其开辟一块空间出来(例外:当这个对象太大或者设置了一个对象阈值-XX:PretenureSizeThreshold,这样对象在分配的时候会直接进入到Old Generation老年代),当我们不断的创建对象时,年轻代的EDEN区可用的区域就会越来越少,终究会有申请完的时候,此时我们就迎来了GC的第一次出场表演。
1.在Young Generation发生的GC通常我们称之为Minor GC,第一次GC时就是找出EDEN区还活着的对象(此时做垃圾回收大概会清理掉EDEN区的80%甚至90%多,因为大部分的对象生命周期并不是那么的长,比如:我们的业务用到了某个集合类,当处理完这段业务时,此集合对象也就随之消亡殆尽)
2.将EDEN还存活的对象移至S0或S1区(这俩是孪生的,移至哪一个均可,暂定为S0),假如此时的S0空间存放满了(因为此区域不是很大,存在满的情况,较少的情况会发生),则放不到S0区的对象将会被放到Old 老年代中
3.第二次EDEN区满的时候,此时我们要做的就是将”EDEN区存活的对象+S0存活的对象”放入到S1区(同样的,假如放不下,剩余的会进入至Old),与第一次GC比较只不过在转移对象的时候多了S0区而已。
4.上述1 2 3步骤周而复始,就形成了年轻代的GC。细心的我们会发现,不论如何GC,S0或S1总有一个区域是空的。
5.反反复复许多次GC之后,若仍有存活的对象(生命力真旺盛啊!),此对象会进入至老年代中,理论上这个反反复复的次数为15,这个数值我们也是可以通过设置默认值来改变的(没有什么是一成不变的)

上图

JVM之堆内存分配

在此分析下Old中为什么没有划分区域的问题:因为能够进入到Old区的对象都是经过了时间的洗礼、岁月的风化过后而来的(就是活的久的老家伙们),不像年轻代中(好多都是昙花一现的家伙,生命力有点脆弱),所以在Old中不会再进行分区。年轻代之所以要分区就是为了在GC后将活着的对象有个单独的地方可以存放,老年代中全是生命力持久的家伙们,所以就不需要再分区了,大家都一样,没有必要再单独给某些家伙开小灶了,就直接都存放在一起吧。
以上就是我目前对JVM之堆内存的分配以及GC的一些分析。谢谢大家,如有什么问题,可以共同探讨。

本人Q:877127905

参考的书:《Java特种兵》-谢宇 电子工业出版社。
参考博客:参考的有点多,就不一一列举了。谢谢!