根节点枚举
固定课作为GC Roots的节点主要在全局性的引用(常量或类的静态属性)与执行上下文(例如栈帧中的本地变量表)中,
但当java应用的扩大,则枚举检查他们非常耗时
所有收集器在根节点枚举时必须暂停用户线程(The World!)(**避免在检查期间用户行为改变引用关系,导致不一致?**个人猜想)
(CMS,G1,ZGC)等收集器,号称不会(几乎不会)发生停顿,但在枚举根节点时也必须要进行停顿
HotSpot的实际做法:
并不是在用户线程停顿时去枚举根节点,而是使用一组OopMap的数据结构,(在类加载时记录数据类型,在编译时记录栈里和寄存器里哪些位置是引用),不是一个不漏的从方法区的GC roots开始查找
安全点
使用OopMap不会为每条指令都生成记录,只在特定位置记录,这些位置被称为‘安全点’
也因为有了安全点的设定,所以用户程序执行时并非在代码指令流的任意位置都能停顿下来做垃圾收集
也就是强制到达安全点后才能暂停开始收集
所以安全点的选定不能太多(多次的暂停,影响程序的长时间运行),也不能太少(垃圾收集器启用的频率太少,或者说是长时间不用导致内存压力变大),需要在两者之间保持平衡。
‘程序长时间运行’的标志为指令序列的复用(如:方法调用,循环跳转,异常跳转。。。。)只有具备这些功能的指令才会出现安全点
垃圾收集发生时,两种线程中断方式:
1.抢先式中断Preemptive Suspension
概念:不需要线程的配合,会先中断所有的线程,再检查线程是否都跑到安全点上,如果发现还没用跑到安全点上,就先恢复其执行然后等其到安全点上再进行收集
2.主动式中断
概念:当垃圾收集需要中断用户线程的时候,不直接去操作线程。而是设计一个标志位(与安全点重合),各个线程在执行过程中会不停的去轮询这个标志位,当标志位为真是,线程将在最近的安全点自动挂起
安全区域(safe region):
原因:安全点机制确保了在程序运行时,在不久的时间后就课遇到课进入垃圾收集的安全点
但**‘程序不运行时’(指线程Sleep或Block时)**,线程无法处理虚拟机的中断请求,
当然更不能等到线程恢复时再进行中断(浪费了时间,也无法进行垃圾收集)
所以引入了安全区域
概念:在一段区域中,任意某个点进行垃圾收集都是可行(安全)的,(可以看作拉伸了的安全点)
内容:当用户线程执行到安全区域的代码时,标记该线程进入安全区域,那么这段时间就不必去管那些声明自己在安全区域的线程
? 先去进行垃圾收集
? 当线程需要离开安全区域时,它需要检查是否完成了根节点枚举(检查完成其他线程的垃圾?),如果完成,线程继续执行当 作没事发生,否则就一直等待,知道可以离开安全区的信号为止
记忆集与卡表
原因:为了解决对象跨代引用的问题,(如:老年代的对象引用了新生代的对象,所以在young gc时是不是应该对老年代进行根节点枚举),为不避免将整个老年代加入GC root的范围,垃圾收集器在新生代中建立了叫‘记忆集’的一种数据结构
记忆集的概念:从非收集区域指向收集区域的指针集合,
记忆集的实现一般为三种不同精度
1.字长精度(每个记录精确到及其字长)
2.对象精度(每个记录精确到对象)
3.卡精度(一块指定大小的内存区域)
其中卡精度也称为卡表,是记忆集实现的最常见的方式
卡表最简单的形式是字节数组,每个元素对应特定大小的内存块,称为‘卡页’
一个卡页中通常包含不止一个对象,
只要卡页存在一个对象的字段存在跨代引用,就将元素标记为1,称为变脏
因而在垃圾回收时,只要知道哪些元素变脏,就知道那些内存页块存在跨代引用,并把他们加入GC root扫描
写屏障
对卡表进行更新维护,
引用操作的切面AOP,
(写前屏障G1实现,写后屏障)
每一次设置引用就会更新卡表
并发的可达性分析(三色标记)
通过Oopmap的优化,找出GC root的需要的停顿减少了(不随着Java堆容量而增长)
但去遍历GC root下的对象图依然会随着堆的增加而增长(堆越大,存储的对象越多,对象图也就更加的复杂,要标记更多的对象,需要的暂停用户线程的时间就会越长)
为了减少在GC root下遍历对象图所花费的时间,所以引入了
三色标记
概念:把遍历对象图中所遇到的对象,按是否被访问过分为三种颜色
白色:表示对象尚未被垃圾回收器访问过(显然在可达性分析的开始阶段,所有对象都是白色,但如果在结束时还是白色,则为不可达)
黑色:表示对象已经被垃圾收集器访问过,且这个对象所有的引用都被扫描过 ? 黑色对象表示已被扫描过,为安全存活状态,引用黑色对象的引用无需再次扫描 ? 黑色对象不可能直接指向白色对象(中间要经过灰色对象)
灰色:表示对象已经被垃圾收集器访问过,但是仍存在一个或多个未扫描的引用
用户线程与垃圾收集器并发工作可能导致的‘对象消失’
赋值器插入了一条或多条从黑色对象到白色对象的新引用
赋值器删除了从灰色对象到白色对象的直接或间接引用
解决方案
增量更新 :即黑色对象插入新的引用后就回退了灰色对象
用户线程与垃圾收集器并发工作可能导致的‘对象消失’
赋值器插入了一条或多条从黑色对象到白色对象的新引用
赋值器删除了从灰色对象到白色对象的直接或间接引用
解决方案
增量更新 :即黑色对象插入新的引用后就回退了灰色对象
原始快照:无论引用关系删除与否,都按照扫描开始时刻的引用关系进行搜索
|