垃圾回收
一、可达性分析
GC ROOT
虚拟机栈中的引用的对象
方法区中静态属性引用的对象
方法区中常量引用的对象
本地方法栈中引用的对象
虚拟机内部的引用,比如基本数据类型对应的Class对象、常驻的异常对象(NullPointException、OutOfMemoryError等)、系统类加载器
GC Roots的生成
1)在类加载完成后,记录对象什么偏移量上是什么类型的数据
2)在安全点,记录下栈和寄存器中哪些位置是引用
安全点
按照是否具有让程序长时间执行的特征为标准选取安全点。如指令序列的复用(方法调用、循环跳转、异常跳转等)特征
通过主动轮训标志位(test指令)的内存保护陷阱方式。
安全点包含轮训标志的地方及创建对象和其它需要在Java堆上分配内存的地方(检查是否将要发生垃圾回收,避免没有足够的内存分配)
安全区域
确保在某一代码片段中,引用关系不会发生变化,因此在安全区域内进行垃圾收集是安全的。
当用户线程进入安全区域后,首先标识自己进入到了安全区域,这样垃圾收集时今不用管这些进入安全区域的线程;当用户线程要离开安全区域时,要检查根节点枚举是否已经完成,完成则可以继续执行,否则等待,直到收到可以离开安全区域的信号。
记忆集(Remebered Set)
记录从非收集区域指向收集区域的指针集合的抽象数据结构。
卡表实现:
卡槽为512个字节,记录非收集区域对象的512字节内存区域是否指向收集区域。
写屏障
虚拟机通过写屏障技术,在引用类型字段赋值动作发生时加入AOP切面实现。
为避免卡表元素的伪共享问题,可使用有条件的写屏障(即判断卡表元素不是Dirty状态是才会更新卡表)
void oop_field_store(oop* field, oop new_value) {
pre_write_barrier(field); // 写屏障-写前操作
*field = new_value;
post_write_barrier(field, value); // 写屏障-写后操作
}
增量更新时写后屏障,也就是引用变化后:
void post_write_barrier(oop* field, oop new_value) {
if($gc_phase == GC_CONCURRENT_MARK && !isMarkd(field)) {
remark_set.add(new_value); // 记录新引用的对象
}
}
SATB是写前屏障,也就是引用变化前:
void pre_write_barrier(oop* field) {
// 处于GC并发标记阶段 且 该对象没有被标记(访问)过
if($gc_phase == GC_CONCURRENT_MARK && !isMarkd(field)) {
oop old_value = *field; // 获取旧值
remark_set.add(old_value); // 记录 原来的引用对象
}
}
读屏障:
void pre_load_barrier(oop* field, oop old_value) {
if($gc_phase == GC_CONCURRENT_MARK && !isMarkd(field)) {
oop old_value = *field;
remark_set.add(old_value); // 记录读取到的对象
}
}
三色标记
使用白灰黑三种颜色标记对象。白色是未标记;灰色自身被标记,引用的对象未标记;黑色自身与引用对象都已标记。
1)对象消失的充要条件:
赋值器删除了所有灰色对象指向该白色对象的直接或间接引用
赋值器添加一个或多个从黑色对象到白色对象的引用
2)对象消失的解决方案:
基于原始快照(SATB):破坏冲要条件的第一条,当删除灰色对象到白色对象的引用时,会将该应用关系记录下来;待并发标记结束后,重新扫描记录中的灰色对象(重新标记)。
可以理解为,无论删除与否,都是基于该开始那一刻的原始快照进行扫描
增量更新:破坏充要条件的第二条,当添加一个从黑色对象到白色对象的引用时,会将该引用关系记录下来;待并发标记结束时,重新扫描记录中的黑色对象(重新标记)。
可以理解为,当添加黑色对象到白色对象的引用时,将黑色对象标记为灰色。
CMS使用的是增量更新,G1使用的是原始快照。
二、回收算法
- 复制
新生代:少量对象存活
- 标记整理
老年代:大多数对象都是存活
标记清除
老年代:大多数对象都是存活
三、垃圾回收器
(一)、Serial
- 新生代
- 复制算法
- 单线程
(二)、ParNew
- 新生代
- 复制算法
- 多线程
- 并行
(三)、Parallel Scavenge
吞吐量 = (业务代码执行时间)/ (业务代码执行时间+GC时间)
- 新生代
- 复制算法
- 多线程
- 并行
- 更关注吞吐量
(四)、Serial Old
- 老年代
- 标记整理
- 单线程
(五)、Parallel Old
- 老年代
- 标记整理
- 多线程
- 并行
- 更关注吞吐量
(六)、CMS
- 老年代
- 标记清除
- 多线程
- 并发
- 更关注停顿时间
初始标记
并发标记
重新标记
并发清理
(七)、GC
- 尝试满足最小停顿时间(可以设置),同时拥有更高的吞吐量
- 逻辑上的Eden、Survived、Old,大对象(H),物理上不分
- 适用于堆内存使用超过50%、对象晋升率高、停顿时间要求高。
初始标记
并发标记
最终标记
筛选回收(堆每个region的排序选择性清理,以期达到期望)