JVM系列 - HotSpot的算法实现
Publish date: Oct 89, 8039
Last updated: Oct 289, 28089
Last updated: Oct 289, 28089
问
HotSpot 虚拟机有以下三个问题需要追溯:
- 如何发起内存回收
- 如何加速内存回收
- 如何保证回收正确性
内存回收 - 发起
根结点枚举
固定可作为 GC Roots 的节点主要在:
- 全局性的引用(常量 或 类静态属性)
- 执行上下文(栈帧中的 本地变量表)
所有收集器在 根节点枚举 这一步都是必须暂停用户线程的,即会造成 STW
如果根结点集合的对象引用关系还不断变化,那么无法保证分析结果准确性
加速枚举
使用 OopMap,其中 Oop 指的是 Ordinary Object Pointer
可以直接让 收集器 直接得到 存放对象引用的 位置
从而避免 一个不漏地检查所有 执行上下文 和 全局的引用位置
安全点
HotSpot 不会为每一条指令都生成 OopMap,否则会占用大量的额外的存储空间
只有在特定位置生成 OopMap,这些位置称为 安全点
用户程序只有在到达安全点后,才能停顿下来,开始垃圾收集
如何在垃圾收集发生时,让所有线程跑到最近的安全点并停顿:
- 抢先式中断:几乎没有虚拟机采用这种方式暂停线程
- 主动式中断:设置标志位,各个线程轮询标志位,各自主动挂起
HotSpot 使用 内存保护陷阱 的方式,保证轮询操作高效
只需要一条汇编指令,就可完成 安全点轮询 和 触发线程中断
安全区域
安全点只保证了:
程序在运行时,在不太长的时间就能遇到 可进入垃圾收集 的 安全点
安全区域确保:
在某一段代码片段中,引用关系不会发生变化
因此在安全区域内的任何地方,开始垃圾收集都是安全的
内存回收 - 加速
记忆集 - 卡表
垃圾收集器在新生代中建立 记忆集,避免把整个老年代加入 GC Roots 扫描范围
通过记忆集,垃圾收集器可以缩减 GC Roots 的扫描范围,加速根结点枚举
内存回收 - 正确性
问题
如果 用户线程 和 收集器 并发工作,收集器在标记时,用户线程在修改引用关系
就会造成 原本存活的对象被错误标记为死亡,或反之
尤其是 存活对象 被标记为 死亡,就会非常致命
解决
- 增量更新(Incremental Update),CMS
- 原始快照(Snapshot At The Beginning,SATB),G1、Shenandoah