整理了一下JVM垃圾回收的分代回收算法,旨在能够以后能够快速熟悉这些算法,而不用去查找大量资料(可以认为是偷懒),也是为了分纤箱一下自己的一些理解,有不足或错误之处,希望大家指正,共同进步!
1.分代回收算法
分代回收算法是标记-复制算法和标记-整理算法(标记-清除)的集合,主要是对新生代和老年代分别进行处理;
在介绍分代回收算法之前首先介绍一下标记-复制算法、标记-清除和标记-整理算法;
1)标记-复制算法:将可用内存分为两部分,只用其中的一部分,当这部分内存用完时,会将存活的对象复制到另一部分内存中,然后把该部分内存清空。这样会导致内存的极大损失,新生代采用了一些优化方法;将新生代的内存分成了一块较大的Eden空间和两块较小的Survivor空间,Eden空间和Survivor空间比例为8;1。每次垃圾回收时,Eden和其中的一块Survivor清空一次把存活的对象放进另一个Survivor里;
2)标记-整理算法:当存活率较高时,复制算法是复制量就会增大,一次对于老年代才用标记-整理算法,将存活的对象想一端移动,然后清除边界以外的所有内存。
2.分代回收算法原理
分代回收分为新生代和老年代两个区域,分别采用复制算法和标记-整理算法;
1)新生代:新生代对复制的内存空间分配进行了优化,新生代区域分为一个Eden区域和两个Survivor区域,默认空间分配为8:1:1;两个Survivor分为from Survivor和to Survivor。a)其中,在Minor GC(复制算法中采用)进行时,将Eden中所有存活的对象移到to Survivor中,from Survivor所有存活的对象会根据它们的年龄决定它们去向,当它们的年龄达到阀值(默认值15,新生代的对象在每次GC一次存活下来,年龄值加一,其分代年龄值存在对象的header中),此对象将被移到老年代;否则,存活的对象复制到to Survivor中;b)然后,Eden和from Survivor区域进行清空,此时,from Survivor和to Survivor进行角色转换,to Survivor变成了之前的from Survivor(在Eden空间存满时,进行GC,移除存活的对象),而from Survivor 变成了之前的to Survivor用来一个空的内存空间来接受Eden和 to Survivor在GC时存活下来的对象。c)当toSurvivor没有足够的空间存储GC存活下来的对象,此时,需要依赖老年代进行分配担保,将这些对象存放在老年代中。
2)老年代:老年代采用标记-整理算法,因为老年代的对象存活率比较高,进行Full GC的频率比较低,而且回收的速度也比较慢(Full GC 一般会比Minor慢10倍)。
3)满足to Survivor的对象向老年代移动的条件
a)当to Survivor 的内存空间不能存储GC所有存活的对象时,会将多余的对象存放到老年代中。
b)当新生代的对象过大时,该对象将直接存储在老年代中。
c)当to Survivor中存活的对象年龄值大于年龄阀值,也将会放进老年代中。
d)动态年龄判断,大于等于某个年龄的对象超过了Survivor中的空间的一半,大于等于该年龄的所有对象经放进老年代。
调优问题:新生代过小,会导致新生代的对象非常快地进入老年代,在老年代中非常难回收;新生代过大会导致每次GC需要复制的对象过多,影响效率。