Java中的垃圾收集器
垃圾回收器分类:
垃圾回收器组合:
传统垃圾回收器组合,
1 | 1. Serial + Serial Old |
查看JVM使用的垃圾回收器命令
1 | -XX:+PrintCommandLineFlags -version |
1 | jps + jinfo |
Serial:
停止用户线程,复制算法,使用一个回收线程
Parallel Scavenge:
停止用户线程,复制算法,使用多个回收线程
ParNew:
停止用户线程,复制算法,使用多个回收线程,配合CMS使用
🐖:上述垃圾回收器由于stop-the-word,程序会产生卡顿
CMS:
并发标记清除,真正意义上的并发收集器,低停顿的垃圾回收器
- 初始标记是STW的,但是时间极短,对root对象进行标记(参考可达性分析)
- 并发标记,标记和root对象相关联的对象,由于程序在运行(可达对象会关联未标记的对象,或产生新的垃圾),可能产生漏标和错标
- 重新标记,将解决错标和漏标的问题,也是STW的,漏标错标对象很少,这部分停顿也很短
- 并发清除,该阶段会产生新的垃圾(称为浮动垃圾),下一次CMS再回收
🐖:并发标记和重新标记阶段采用三色标记法
GC roots: 线程栈变量,静态变量, 常量池, JNI指针
1 | 当服务器内存很小时,使用单线程Serial收集没问题,停顿时间很小;当内存变大时,出现了多线程版本的Parallel,此时也能接受应用的停顿;当内存再变大时,STW的方式使得服务器停顿时间过长,于是CMS登场了,并发地清理垃圾,然后CMS也有不足之处,会产生大量内存碎片。 |
CMS的缺点:
采用mark sweep算法,会产生很多内存碎片,当内存区域不足时,需要进行FullGC,CMS的FullGC很耗时,导致应用停顿很长时间
老年代GC的问题:
操作必须扫描整个老年代,不适合大内存
young和old区都是独立的内存块,大小必须提前确定
G1
G1是一种服务端应用使用的垃圾收集器,目标是用在多核,大内存的机器上,它在大多数情况下可以实现指定的GC暂停时间,同时还能保持较高的吞吐量
特点
- 并发收集
- 压缩空闲空间不会延长GC的暂停时间
- 更易预测的GC暂停时间
- 适合不需要实现很高的吞吐量的场景
概要
物理上不分代,逻辑上分代,内存区域被分为大小相同的区域(Region),作为Survivor,Eden,和Old,每一个块大小为1M~32M(2的倍数,-XX:G1HeapRegionSize),另外大对象放在Humongous。G1的内存区域并不是固定的,当前被设置为Eden,被回收后,下次可能被分配为Old。垃圾收集线程可以并发地运行在这片区域,进行垃圾回收
Why G1
- 追求响应时间
- -XX:MaxGCPauseMillis 200
- 对STW进行控制
- 灵活
- 分Region回收
- 优先回收花费时间少、垃圾比例高的Region
Eden,Survivor 和 humongous 区域特点
**新老年代比例 5% ~ 60%**,一般不用手动指定
humongous object:超过单个region的50%
对象何时进入到老年代
G1 如何进行垃圾回收(mixedGC)
- 初始标记 STW
- 并发标记
- 最终标记 STW
- 并发回收 STW (复制回收 【5%的内存区域用来作复制回收】)
三色标记法
1. 产生漏标
原因:
黑色对象A 指向白色对象D,原来指向白色对象的灰色对象B不再指向白色对象D。
因为A已经完成自身标记和fields标记,B也不再扫描D,所以产生漏标
解决方案
CMS:增量标记,引用增加时,将该对象设置为灰色(上图A从黑色设置为灰色)
G1:引用删除时,将该引用记录下来,保证该对象还能被扫描到(记录D的地址)