垃圾收集算法
分代收集理论
根据不同代选用不同的算法,年轻代用一种算法,老年代用一种算法
标记-复制算法
分为两个区,一个区空闲,一个区放对象,做gc时标记垃圾对象或者标记非垃圾。把非垃圾对象整齐的复制到空闲区。复制完成后一次性清理掉。
标记-清除算法
标记非垃圾对象,把剩余空间全部清除;
存在问题:空间碎片化,对象的分配需要整块的空间;
可能存在很多对象,需要标志很长时间。
标记-整理算法
标记非垃圾对象,将非垃圾整齐的挪(复制过去,先不管垃圾对象,如果有垃圾对象,直接覆盖)到角落,再去销毁垃圾对象。空间规整;
垃圾收集器分类
Serial:串行,单线程,可用于年轻代和老年代;适合单核CPU,一般没什么用了。
Parallerl:并发,多线程,可用于年轻代和老年代,不能和CMS配合使用。适用于2G-3G这样大小的内存;
ParNew:用于年轻代并发多线程,与Parallerl几乎没什么区别,但是可以和CMS配合使用;
CMS:适合比较大容量的内存,目前主流老年代垃圾收集器和ParNew配合使用;
CMS垃圾收集过程:
1、初始标记:标记所有gc roots直接引用对象(停止其他所有线程,启动STW,因为只找直接引用对象,所以效率很高);(程序不断在运行,不断有新的变量引用新的对象,所以要停止)
2、并发标记:运行标记(三色标记中的增量更新法),找完堆里所有引用对象的同时,允许用户线程一起运行。(时间很长,占到CMS回收垃圾时间的80%,但是因为和用户线程同时运行,用户感知不到)
3、重新标记:修正并发标记时运行程序产生的变动,这个时间也很难段只会比初始标记长一点点;(停止用户所有线程)
4、并发清理:开启用户的线程,并清理未标记的对象,如果这个时间添加新得对象会被标记成黑色不会做清理;
5、并发重置:重置本次GC过程中的标记数据;
缺点:
1、用户线程和垃圾回收机制会争抢CPU资源
2、无法处理浮动垃圾(详细,增量更新有说)
3、回收算法使用 “标记清除”,会产生大量的碎片,但是可以设置参数,开启碎片整理;
4、并发模式失败:做full gc就是因为老年代没什么空间了,所以在做并发标记或者并发处理时,用户线程还是在跑,万一这个时候发来一个大对象就会导致变成单线程的垃圾回收机制。会变得非常慢。我们可以通过参数设置,比如设定当老年代得空间达到90%的时候就开始做full gc;
cms参数:(不需要知道参数怎么写,到时候查,知道有什么参数即可)
(带有-X参数表示不稳定参数,X越多代表命令越不稳定,这些参数很可能在以后的版本就会改变)
启用cms;
并发的GC线程数(一般不用改变,他会根据cpu的核数来自动设置);
启用标记整理;
多少次full gc做一次标记整理;
设置老年代百分之多少可以做full gc;
设置自动调整老年代百分之多少时full gc,只有第一次根据上面那个参数走(这样更好);
设置cms full gc之前先做一次minor gc,减少full gc时间(一般关系不大,没啥用);
设置初始标记为多线程;
设置重新标记的时间;
三色标记:
黑色:表示这个对象及其所有引用的对象都被扫描了;
灰色:已经被gc访问过了,但是至少还有一个引用没有被扫描;
白色:从未被访问过,标记为垃圾对象;
增量更新:
不是垃圾的对象,并发标记后又变成垃圾对象。原本是垃圾的对象后来又变成非垃圾对象(非常严重的BUG)。在并发标记的时候,对那些新增的引用(做赋值)的源头重新标记为灰色,等到重新标记的时候,就会重新扫描,把这些对象都变成黑色。这样在清理的时候就不会被清理了。无法处理那些非垃圾对象变成垃圾对象,只能依靠下次GC的时候处理,这也是会产生浮动垃圾的原因。(重新标记更是会对在并行标记新生成的gc roots做扫描,下面的原始快照也一样)
原始快照
灰色对象要删除指向白色对象的引用时,通过写屏障记录这一引用,在重新标记的时候,以这个灰色对象为根重新扫面就能扫描到这个白色对象,然后把这个白色对象变黑(很可能就变成了浮动垃圾)
写屏障
类似AOP切面编程,在写的时候可以插入一些代码;在写前写后都可以做操作;
记忆集与卡表
记忆集相当于卡表的接口,卡表算是记忆集的实现;
年轻代做minor gc的时候,也要考虑到老年代中对年轻代的引用,但是老年代的东西的是很大的,而且一般老年代对年轻代的引用是非常少的。如果每次做minor gc的时候都去扫描一次老年代非常的消耗时间,所以就有了记忆集这种东西,把老年代的对年轻代的引用放在一个集合里,当做minor gc的时候就去扫描这些集合里对象的引用;
卡表实现记忆集具体情况:老年代划分一块一块小区域(512字节),称为页内存也就是card,只要卡页其中有一个对象引用了年轻代的对象,就把卡页标记为dirty(脏的);我们做minor gc的时候就要去扫描,Cradtable(卡表),底层实现就是01010101之类的,0表示不脏,1表示脏,也记录每个卡页的内存地址;