堆(Heap)是被虚拟机所管理的最大的一块内存区域,在堆中,会有以下一些对象:
- 朝生夕死的小对象,蜉蝣一般
- 大对象,例如长数组,需要大量连续的内存空间
- 长周期对象,存活很久,很能熬
因此,目前主流的JVM,利用可达性分析算法分析对象是否死亡,最后针对性地采用分代搜集算法回收死亡对象。判断对象是否死亡,可以先参考我的另外一篇文章【JAVA】如何判断对象已经死亡?
堆可以分为新生代与老年代,用来存放不同类型的对象。如下图所示:
新生代
新生代含有3个区域,1个Eden区,2个Survivor区,可以分别称为from以及to区,from与to的大小相同。它们占用的大小比例默认为Eden:from:to=8:1:1,当然也可以通过参数-XX:SurvivorRatio来自定义比例。
新创建的对象都会处在Eden区,大对象会直接进入老年代,可以使用参数-XX:+PretenuerSizeThreshold来指定多大的对象。
新创建的对象优先在Eden区上分配,Eden区满了之后,会触发一次Minor GC,虚拟机会采用复制算法(不会产生内存碎片)先进行释放内存,回收死亡对象,然后将存活的对象一次性复制进from区域中。若from区域不够,则使用分派担保机制将部分对象直接推入老年代。老年代空间不够,将触发一次Full GC。
发生过一次Minor GC之后,Eden区域基本空闲,新创建的对象依然在这块区域上分配,当Eden区域又满了之后,同样还是出发第二次Minor GC,这次GC将会回收Eden与from区域,将存活的对象一次性地复制进to区域中。
这些对象在新生代中每熬过一次Minor GC,对象的年龄就会加1,当年龄达到某一个阈值时(默认为15),将会被直接移到老年代中,可以使用参数-XX:MaxTenuringThreshold来指定这个阈值。
因为新生代区域上对象,创建频繁,死亡也频繁,因此Minor GC也会变得十分频繁,但是这种GC方式效率高,持续时间短。
老年代
老年代中主要有老年对象(超过年龄阈值的对象)以及大对象。当老年代空间不足时,会触发一次Full GC,由于各个垃圾收集器的实现不同或处于效率考虑,可能会采用标记清除算法,也可能采用标记整理的算法回收死亡对象。
当然,也不是对象必须达到年龄阈值才会进入老年代中,如果Survivor内某个相同年龄下(比如10岁)的所有对象的大小总和超过Survivor区的一半,那么年龄≥10岁的对象将直接进入老年代中。
Full GC不像Minor GC那么频繁,但持续时间长。倘若频繁发生Full GC,会严重阻碍应用程序的执行。
也有一些博客会将Full GC与Major GC区分开来,认为他们的区别是Full GC会对整个堆进行回收,而Major GC只会对老年代进行回收。但其实现在已经不区分这两者了,很多性能监控工具也不再区分,现在大可认为Full GC与Major GC没有差别。
常用参数
(1)新生代初始大小:-XX:NewSize,最大:-Xmn(-XX:MaxNewSize)
(2)Eden区与1个Survivor区的比例:-XX:SurvivorRatio
如果新生代的大小为10M,新生代=1个Eden+2个Survivor,并且设置-XX:SurvivorRatio=8,那么此时Eden区占用8M,每个Survivor区各占1M。
(3)老年代初始大小:-XX:OldSize
(4)老年代与新生代的比例:-XX:NewRatio
如果设置堆总大小为100M,堆=新生代+老年代,并且设置-XX:NewRatio=4,那么老年代大小是新生代的4倍,此时新生代占20M,老年代占80M。
(5)打印GC信息:-XX:+PrintGCDetails
(6)在GC前后打印堆信息:-XX:+PrintHeapAtGC
(7)设置进入老年代的年龄阈值:-XX:MaxTenuringThreshold
(8)设置直接进入老年代的大对象的大小阈值:-XX:+PretenuerSizeThreshold
Minor GC的触发条件
(1)Eden区空间不足,或者说Eden区放不下新创建的小对象。
Full GC触发条件
(1)老年代空间不足,此时执行Full GC后,老年代空间依旧不足的话,虚拟机会抛出 java.lang.OutOfMemoryError: Java heap space异常
(2)调用System.gc()方法,这只是通知或者是建议虚拟机进行Full GC,虚拟机可以根据情况选择是否执行。
(3)Minor GC时,将对象从Eden区与from区向to区转移,若to区空间不够,此时会将部分对象转移到老年代中,若此时老年代空间不够,将触发一次Full GC。
(4)永久代满(JDK1.7之前,方法区的实现-永久代还未被元空间取代)
关于分配担保机制与GC实验,可能要另开篇幅