新生代垃圾回收
之前的两篇文章(java执行时数据区浅析、java垃圾收集器(GC)浅析)介绍了java执行时数据区以及GC垃圾回收算法的相关知识,大家能够先去看看这两篇文章。
本篇文章将重点关注新生代的垃圾回收。
大部分JVM都会採用所谓的分代收集方式去回收垃圾,什么是分代收集呢?
依据对象的存活周期的不同将内存划分为好几块。通常是把java堆分为新生代和老年代,这样就能够依据各个年代的特点採用最适合的收集算法。新生代中,每次垃圾收集时都发现大批对象死去,仅仅有少量存活,那就选用复制算法。老年代由于对象存活率高,没有额外空间对它进行分配担保,那就必须使用“标记-清理”或者“标记-整理”等重量级算法来进行回收。
单纯从JVM的功能考虑,并不须要新生代,全然能够针对整个堆进行操作。新生代存在的唯一理由是优化垃圾回收的性能。更详细的说,把堆划分为新生代和老年代有两个优点:
1.简化了新对象的分配(仅仅在新生代分配内存);
2.能够更有效的清除不再须要的对象(即死对象,新生代和老年代使用不同的回收算法)。
通过广泛研究面向对象实现的应用,发现一些特点:
1.大部分对象的生存时间都非常短;
2.新生对象非常少引用生存时间长的对象。
所以,新生代通过复制算法能够高效的回收垃圾。
依据上面的思想,我们继续将堆区域划分成例如以下部分:
首先堆能够划分为新生代和老年代,然后新生代又能够划分为一个Elden区和两个Survivor(幸存)区。
依照规定,新对象会首先分配在Eden中(假设对象过大,比方大数组,将会直接放到老年代)。在GC中,Eden中的对象会被移动到survivor中,直至对象满足一定的年纪(定义为熬过minor GC的次数),会被移动到老年代。
新生代採取复制算法,在Minor GC之前,to survivor区域保持清空,对象保存在Eden和from survivor区,minor GC执行时,Eden中的幸存对象会被拷贝到to Survivor(同一时候对象年龄会添加1)。而from survivor区中的幸存对象会考虑对象年龄,假设年龄没达到阈值,对象依旧拷贝到to survivor中。假设对象达到阈值那么将被移到老年代。复制阶段完毕后,Eden和From幸存区中仅仅保存死对象,能够视为清空。假设在复制过程中to幸存区被填满了,剩余的对象将被放到老年代。最后,From
survivor和to survivor会调换一下名字,下次Minor GC时,To survivor变为From Survivor。
步骤例如以下图所看到的:
总结一下,对象一般出生在Eden区,年轻代GC过程中,对象在2个幸存区之间移动,假设对象活到适当的年龄,会被移到老年代。当对象在老年代中死亡时,就须要更高级别的GC,更重量级的GC算法。
调优的重要性:新生代的大小设置非常重要,假设新生代过小,会导致新生对象非常快就晋升到老年代中,在老年代中对象非常难被回收。假设新生代过大,会发生过多的复制过程。因而我们须要找到一个合适的大小,不幸的是,要想获得一个合适的大小,仅仅能通过不断的測试调优,这就须要JVM參数了。