GC的垃圾清除算法
- 标记清除算法:从GC Root遍历标记,未被标记的清除
- 标记整理算法:从GC Root标记遍历,将标记到的对象整理至内存一侧,然后清除某一地址后的对象
- 复制算法:将内存一分为二,只是用其中一个内存作为活动区,未被使用的为空闲区,从GC Root标记遍历,被标记的算法从活动区规则的排列在空闲区,然后将活动区清空,交换空闲区和活动区,在对象存活率低的时候使用
- 分代搜集算法
以上三种GC算法都需要在程序停止时执行,否则会造成刚new出来的对象,直接被回收的情况,而且还要遍历堆上的全部对象,因此效率比较低,然后就有了克服以上缺点的GC算法,分代搜集算法:
内存中的对象按生命周期的长短分为三类:
- 新生代(Young Generation):刚new出来的对象都是新生代,大部分循环变量为方法的局部变量,循环的临时变量等等,如果一个新生代对象历经几次搜集后没有被清除,则转变为年老代
- 年老代(Old Generation):单例对象等等
- 持久代((Permanent Generation):String池中的对象(享元模式)、加载过的类信息等等
在JVM的内存划分中,新生代和年老代都在JVM的堆(Heap)中,而持久代在方法区中(静态代码块中new的对象),而JVM规范对方法区中的类并不做规范,因此分代搜集算法只针对堆中的新生代对象和年老代对象.
对新生代对象,新生代的存活率较低,因此采用复制算法,但是并不是完全照搬,而是进行改良赋值算法,在内存划分上,将原来的空闲:活动 1:1,改为8:1:1,两个'1'分别为空闲区和活动区,另外的8给new的对象使用,在进行清除时,将活动区1和8中的存活对象移至空闲区1中,然后将9中的对象全部清除,这个算法的前提是对象的存活率必须要10%以下,不然内存空间不足以将所有的存活对象全部移至活动区,但这样做的好处是,只有10%的内存空间浪费.
为了解决上述10%内存不够的问题的发生,我们将将java堆分为两部分来处理,新生和年老,上述方法适用于新生代对象.那么新生代是怎么升为年老代的那?有两种方式
1:在新生代里的每一个对象,都会有一个年龄,当这些对象的年龄到达一定程度时(年龄就是熬过的GC次数,每次GC如果对象存活下来,则年龄加1),则会被转到年老代,而这个转入年老代的年龄值,一般在JVM中是可以设置的.
2:在新生代存活对象占用的内存超过10%时,则多余的对象会放入年老代.这种时候,年老代就是新生代的:备用仓库".
针对老不死对象的特性,显然不再适合使用复制算法,因为它的存活率太高,而且不要忘了,如果年老代再使用复制算法,它可是没有备用仓库的.因此一般针对老不死对象只能采用标记/整理或者标记/清除算法.
进行GC的时
- 普通GC:只针对新生代
- 全局GC:针对年老代,偶尔新生代和持久代