- 系统在运行的过程中,会创建许许多多的对象,这些对象会经历生老死的历程,而那些’死’的对象需要进行回收为新生对象提供空间.
# 前言
- 系统在运行的过程中,会创建许许多多的对象,这些对象会经历生老死的历程,而那些’死’的对象需要进行回收为新生对象提供空间.
# 回收算法
# 引用计数算法
# 描述
- 每个对象均配有一个引用计数器,当对象被引用一次,计数器 + 1, 反之移除对该对象的引用时,计数器 - 1, 当值为 0 时,将被 GC 列为回收对象.
# 优点
- 算法简单
# 缺点
- 当出现循环依赖时,造成彼此间计数器不为 0, 则不能被 GC 回收,因此产生内存泄漏问题
# 例子
# 代码
public class ReferenceCountTest { | |
public static void main(String[] args) { | |
ReferenceObject object01 = new ReferenceObject(); // Step 01 | |
ReferenceObject object02 = new ReferenceObject(); // Step 02 | |
object01.setInstance(object02); // Step 03 | |
object02.setInstance(object01); // Step 04 | |
object01 = null; // Step 05 | |
object02 = null; // Step 06 | |
} | |
} | |
class ReferenceObject { | |
private Object instance; | |
public ReferenceObject setInstance(Object instance) { | |
this.instance = instance; | |
return this; | |
} | |
} |
# 图解

# 分析
- Step 01: ReferenceObject01 实例引用计数 + 1 计数器 01=1
- Step 02: ReferenceObject02 实例引用计数 + 1 计数器 02=1
- Step 03: ReferenceObject02 实例引用计数 + 1 计数器 02=2
- Step 04: ReferenceObject01 实例引用计数 + 1 计数器 01=2
- Step 05: ReferenceObject01 实例引用计数 - 1 计数器 01=1
- Step 06: ReferenceObject02 实例引用计数 - 1 计数器 02=1
# 结果
- 循环依赖时,造成彼此间计数器不为 0, 则不能被 GC 回收,因此产生内存泄漏问题
# 可达性分析算法
# 描述
- 从 GC Roots 节点向下搜索,所走的路径称之为’引用链’, 当一个对象对 GC Roots 节点没有任何引用链相连接时,则认为该对象是不可用的 (即使该对象还与其他对象相关联).
# 例子
# 代码
public class FinalizeEscapeGC { | |
public static FinalizeEscapeGC HOOK = null; | |
public void log(){ | |
System.out.println("I am still alive."); | |
} | |
public static void main(String[] args) throws Throwable{ | |
System.out.println("gc roots tracing start."); | |
HOOK = new FinalizeEscapeGC(); | |
HOOK = null; | |
/** 对象不可达,进行自救 {@link FinalizeEscapeGC#finalize ()}, finalize () 仅且执行一次。自救成功 */ | |
System.gc(); | |
TimeUnit.SECONDS.sleep(1); // 执行筛选 'F-Queue' 的线程优先级较低,休息一下... | |
if(Objects.nonNull(HOOK)){ | |
HOOK.log(); | |
}else{ | |
System.out.println("oh, I am dead."); | |
} | |
HOOK = null; | |
/** 对象不可达,进行自救,因 finalize () 仅且执行一次,将不再执行,自救失败 */ | |
System.gc(); | |
TimeUnit.SECONDS.sleep(1); // 执行筛选 'F-Queue' 的线程优先级较低,休息一下... | |
if(Objects.nonNull(HOOK)){ | |
HOOK.log(); | |
}else{ | |
System.out.println("oh, I am dead."); | |
} | |
System.out.println("gc roots tracing end."); | |
} | |
@Override | |
protected void finalize() throws Throwable { | |
super.finalize(); | |
System.out.println("help me!!!"); | |
HOOK = this; | |
} | |
} |
# 输出
gc roots tracing start. | |
help me!!! | |
I am still alive. | |
oh, I am dead. | |
gc roots tracing end. |
# 分析
- 经可达性分析算法所标记的对象,会经过第一次标记与筛选 (有没有必要执行 finalize () (对象有没有覆盖 finalize () 方法)):
- 有必要,将对象放入 F-Queue 队列中,稍后由 Jvm 自动创建一个低优先级的 FinalizerThread 执行扫描该队列,进行第二次筛选 (执行 finalize () 俗称:自救), 对象在该过程期间是否与引用链上的任何一个对象建立关联:
- 有,从回收集合中移除;
- 没有,进行回收
- 没有必要,加入回收集合;
- 有必要,将对象放入 F-Queue 队列中,稍后由 Jvm 自动创建一个低优先级的 FinalizerThread 执行扫描该队列,进行第二次筛选 (执行 finalize () 俗称:自救), 对象在该过程期间是否与引用链上的任何一个对象建立关联:
# 使用 finalize 注意
- 因 FinalizerThread 是一个低优先级的线程,启动后 F-Queue 队列中的元素排队执行 finalize () 方法,使用不合理情况下,将导致这些对象长时间堆积在内存中,到达已经程度可能会引发 OOM;
# GC Roots 对象包含 (但不限于)
- 启动类 (Bootstrap) 加载的类和创建的对象;
- 栈中引用的对象 (栈帧中的本地变量表引用的对象);
- 元数据区类静态属性引用的对象;
- 元数据区常量引用的对象;
- JNI (Native 方法) 引用的对象;
# 引用
- 分为,强引用,软引用,弱引用,虚引用。引用强度依次逐渐减弱
# 强引用 (Strong Reference)
- 类似 Object obj = new Object ();, 可以直接访问目标对象,强引用执行的对象在任何时候都不会被系统回收 (Jvm 宁愿抛出 OOM, 也不会回收强引用所指向对象,因此可能会导致内存泄漏);
# 软引用 (Soft Reference)
- SoftReference 来实现软引用,用来描述一些还有用但非必需的对象,在系统要发生内存溢出之前,将这些对象列入回收范围进行回收。若回收后还不足用于创建新对象的空间,则抛出 OOM;
# 弱引用 (Weak Reference)
- WeakReference 来实现弱引用,用描述一些非必需的对象,只能生存到下一次垃圾收集发生之前 (无论内存是否充足,都是回收);
# 虚引用 (Phantom Reference)
- PhantomReference 来实现虚引用,对象是否有虚引用的存在,完全不会对其生存时间构成影响,无法通过虚引用来取得一个对象实例;
- 作用:可以在垃圾收集器回收时收到一个通知;
# 垃圾回收算法
# 标记 - 清除算法
# 描述
- 标记存活的对象;
- 统一回收未标记的对象;

# 缺点
- 效率问题 (执行期间会 Stop The World)
- 空间问题 (容易产生大量不连续的空间碎片,无法分配大对象,会导致频繁 Minjor GC, 以至导致 Full GC, 最终 OOM)
# 总结
- 适用于单核机器上;
# 标记 - 整理算法
# 描述
- 该算法是对’标记 - 清除算法’的优化;
- 标记存活对象;
- 存活对象向一端一端,清理存活边界以外内存;

# 优点
- 不会产生内存碎片;
# 总结
- 该算法适用于对象’存活率高’的区域;
- 适用于老年代区;
# 标记 - 复制算法
# 描述
- 标记存活对象;
- 将存活对象复制到另外一半内存;
- 清除当前这一半的内存区域;

# 优点
- 对象存活率低,复制的对象就少,进而效率高 (每次最多浪费 10% 的内存 (Eden : Survivor From : Survivor To = 8 : 1 : 1));
# 缺点
- 可使用内存只有一半 (该算法不适合对象存活率较高场景,老年代一般不采用该算法);
# 总结
- 该算法适用于对象’存活率低’的区域;
- 适用于新生代 (Eden) 区
# 垃圾收集器
# Serial 收集器

# 描述
- 基于’标记 - 复制算法’的单线程收集器;
- 单线程收集器,执行期间用户线程暂停 (俗称:Stop The World(STW));
# 使用方式
-XX:+UseSerialGC
# 总结
- 适用于新生代;
- 在单核机器上适用场景较多;
# ParNew 收集器

# 描述
- 基于’标记 - 复制算法’ ' 并行’的多线程收集器 (Serial 收集器的多线程版本);
- 以获取最短回收停顿时间为目标的收集器;
- 默认垃圾收集线程数与 CPU 数量相同;
# 使用方式
-XX:+UseParNewGC
-XX:ParallelGCThreads=n // 设置垃圾收集的线程数
# 总结
- 适用于新生代;
# Parallel Scavenge 收集器
# 描述
- 基于’标记 - 复制算法’ ' 并行’的多线程收集器;
- 以吞吐量优先的收集器 (运行用户代码时间 /(运行用户代码时间 + 垃圾回收时间));
# 使用方式
-XX:+UseParallelGC
-XX:MaxGCPauseMillis=n // 设置最大垃圾收集停顿时间 n(ms) (停顿时间缩短是以牺牲吞吐量和新生代(Eden)区来换取), 过小会导致频繁GC, 默认: 200
-XX:GCTimeRatio=n // 设置吞吐量大小 n%
-XX:+UseAdaptiveSizePolicy // 自适应调节策略(根据当前系统运行情况收集性能监控, 动态调整'-Xmn','-XX: SurvivorRatio', '-XX: PretenureSizeThreshold'以提供最合适的停顿时间和最大的吞吐量)
# 总结
- 适用于新生代;
- 未知情况下,可使用自适应调节策略来动态调整 (出现问题时,会增大排除问题难度…);
# Serial Old 收集器
# 描述
- 基于’标记 - 整理算法’的单线程收集器;
- 单线程收集器,执行期间用户线程暂停 (俗称:Stop The World(STW));
# Parallel Old 收集器
# 描述
- 基于’标记 - 整理算法’ ' 并行’的多线程收集器;
- 以吞吐量优先的收集器
# 使用方式
-XX:+UseParallelOldGC
# CMS (Concurrent Mark Sweep) 收集器

# 描述
- 以获取最短回收停顿时间为目标的收集器;
- 基于 标记 - 清除算法实现的;
- CS (Collection Set): 一组可被回收的 Region 的集合;解决并发标记阶段,Old -> Young 或 Young -> Old 跨代引用的问题;
- Points-Out (我引用了谁的对象)
- 步骤:
- 初始化标记 (STW: 速度很快)
- 并发标记
- 预清理
- 可被终止的预清理
- 重新标记 (STW: 持续时间最长)
- 并发清除
- 并发重置状态
# 初始化标记 (STW)

- 标记老年代所有 GC Roots 对象 (从根集合可直接到达的对象);
- 标记年轻代所有 GC Roots 引用老年代的对象;
- 该阶段是基于单线程的,可通过 "-XX: +CMSParallelInitialMarkEnabled" 和 ‘-XX: ParallelGCThreads=n’ 来调改变单线程的操作,开启并行处理;
# 并发标记

- 从 GC Roots 进行追踪 (tracing), 找出并标记所有存活对象;
- 因该阶段与用户线程并发执行,可能会引发 "Concurrent Model Failure";

- 该阶段是与用户线程并发执行的,运行过程中,对象可能存在多种情况: “新生代对象普生至老年代”, “大对象直接进入老年代”, “老年代中对象间引用关系变动”. 为杜防这些情况发生,会将这些对象重新标记,对象对应 Card 上的标识改为 Dirty(并发标记阶段只负责标识,重新标记阶段会只扫描这些的对象,避免扫描整个老年代.);
# 预清理

- 处理并发标记阶段 Card 上标识为 Dirty 的对象;
# 可被终止的预清理
- 尝试分担重新标记阶段的工作;
- 该阶段是重复做相同的事情,直到发生 abort 的条件 (重复次数,持续时间等), 可通过 "-XX:CMSMaxAbortablePrecleanTime=n" , “-XX:CMSScheduleRemarkEdenPenetration=n”, "-XX:CMSScheduleRemarkEdenSizeThreshold=n" 等参数来调整;
# 重新标记 (STW)
- 标记老年代所有的存活对象,重新扫描整个堆;
- 因老年代中的对象可能被新生代中对象引用,所以需要扫描新生代;
- 因扫描时,会使用新生代中不可达的对象当做 "GC Roots" 对象,来扫描老年代。当这些不可达的对象引用老年代中对象太多情况下,会导致该阶段耗时过长 (实际这些扫描是没必要的). 出现这种情况下可通过 "-XX: +CMSScavengeBeforeRemark" 来优化该阶段,在重新标记前,先执行一次 "YGC", 回收不可达的对象,可达的对象移入幸存区 (Survivor) 或老年代,以减少年轻代对老年代的无效引用,再执行重新标记时,只需扫描幸存区即可,这样就减少了扫描时间.
- 因老年代中的对象可能被新生代中对象引用,所以需要扫描新生代;
- 该阶段是基于单线程的,可通过 "-XX: +CMSParallelRemarkEnabled" 开启并行处理;
# 并发清除
- 根据清除算法清除不可用对象回收内存空间;
- 因基于 " 并发 - 标记 - 清除 "算法,清除后不会对内存做整理,可能就会出现内存碎片,造成 OOM, 可通过"-XX: +UseCMSCompactAtFullCollection"和"-XX: CMSFullGCsBeforeCompaction=n" 指定执行’n’次后做一次内存整理 (默认: 0, 每次 Full GC 都会执行内存整理);
- 因该阶段与用户线程并发执行,可能会产生新的垃圾,这部分垃圾称为 "浮动垃圾", 留在下次 GC 时清除;
# 并发清除
- 清除内部状态
# 使用方式
-XX:+UseConcMarkSweepGC // 开始使用CMS垃圾收集器
-XX:+CMSScavengeBeforeRemark // "重新标记"阶段前, 先执行一次YGC
-XX:+ScavengeBeforeFullGC // "并发清除"阶段前, 先执行一次YGC
-XX:UseCMSInitiatingOccupancyOnly // 配置JVM不基于运行时收集的数据来动态调整垃圾收集周期(关闭自适应调节策略)
-XX:CMSInitiatingOccupancyFraction=n // 配置JVM在老年代内存占用达到n%时触发GC(需搭配"-XX: UseCMSInitiatingOccupancyOnly"使用, 否则仅在第一次使用该配置, 后续依然使用自适应调节策略) 默认: 92
-XX:+UseCMSCompactAtFullCollection // 开启对老年代进行内存整理, 以消除内存空间碎片化
-XX: CMSFullGCsBeforeCompaction=n // 执行n次后进行内存整理
-XX:+CMSParallelInitialMarkEnabled // 初始化标记阶段开启并行操作
-XX:+CMSParallelRemarkEnabled // "重新标记"阶段开启并行操作
-XX:ParallelGCThreads=n // 并发回收线程数, 默认: (cpus <= 8) ? cpus : 3 + ((cpus * 5) / 8)
-XX:CMSMaxAbortablePrecleanTime=n // 预清理阶段, 最大持续时间(ms) 默认: 5000
-XX:CMSScheduleRemarkEdenPenetration=n // "可被终止的预清理"阶段, Eden区域内存达到n%后, 进入"重新标记阶段"阶段, 默认: 50
-XX:CMSScheduleRemarkEdenSizeThreshold=n // "可被终止的预清理"阶段, Eden区域内存达到nM后, 进入"重新标记阶段"阶段, 默认: 2
-XX:+CMSIncrementalMode // (i-cms)增量的进行垃圾收集, 并发阶段, 不会独占整个周期, 周期性暂停, 唤醒应用线程. 收集器把并发工作划分为片段, 安排在次级(minor)回收间执行(在少量CPU, 需低延迟服务器上优势会高些).
-XX:+CMSIncrementalPacing // 开启基于运行时收集的数据动态调整垃圾回收任务的数量
-XX:CMSIncrementalDutyCycle=n // 每次增量回收垃圾占总垃圾的比例 默认: 10
-XX:CMSIncrementalDutyCycleMin=n // 每次增量回收垃圾站总垃圾的最小比例 默认: 0
-XX:ExplicitGCInvokesConcurrent // System::gc时做background模式的Full GC(默认会STW)
-XX:+DisableExplicitGC // 禁止在运行期间显式调用System::gc
-XX:-ReduceInitialCardMarks // 禁止并发初始化新对象与RS更新放到一起执行
# 问题
# Concurrent Model Failure
- GC 执行过程中,用户线程也在运行,当年轻代空间不足时触发 "Minjor GC", “Minjor GC"执行过程中可能会有幸存者进入老年代,此时老年代内存不足,然后就抛出"concurrent mode failure”;
- 可通过 "-XX: +UseCMSInitiatingOccupancyOnly" 和 "-XX: CMSInitiatingOccupancyFraction=n" 合理配置来解决该问题;
# Promotion Failed
- GC 执行过程中,“Survivor Space"空间不足,对象直接进入老年代,此时老年代内存不足,然后抛出"promotion failed”(可能是老年代空间足够,是碎片化的,转入老年代的对象找不到连续的空间存放导致的);
- 过早提升: Minor GC 过程中,“Surevivor To” 不足以存储 "Eden" 和 "Surevivor From" 中存活的对象,多余的对象将被移入老年代 (该种情况可调整 Survivor 空间大小);
- 视情况而定,可通过 "-XX: CMSFullGCsBeforeCompaction=n" 进行空间整理…
# 缺点
- 只回收老年代,以吞吐量换取停顿时间;
- 应用长时间运行情况下,容易造成严重的内存碎片化 (可解决);
- 无法解决浮动垃圾问题;
# G1 收集器

# 描述
使用相对较短的停顿时间来达到最大吞吐量的收集器;
SATB(Snapshot At The Beginning): 并发标记阶段,用户线程与垃圾收集线程并发执行,就可能出现突变者 (对象被引用或取消引用), 根据 " 三色标记法 " 维护并发 GC 的正确性;
- 白色:对象没有被标记到,标记阶段结束后,会被当做垃圾回收掉;
- 灰色:对象已被标记,但里面还有 field 没有被标记;
- 黑色:对象已被标记,且里面所有 field 也已被标记;
TAMS (Top At Mark Start): 解决并发标记阶段,新对象被 new, 但未被引用,而导致漏标的情况;
- 并发标记中用到的两个 Bitmap
- prev Bitmap: 记录第 n-1 轮 Concurrent Marking 所得的对象存活状态. (由于第 n-1 轮 Concurrent Marking 已经完成,这个 Bitmap 的信息可以直接使用);
- next Bitmap: 记录第 n 轮 Concurrent Marking 的结果. (这个 Bitmap 是当前将要或正在进行的 Concurrent Marking 的结果,尚未完成,所以还不能使用);
- Region 中几个指针
![垃圾收集器-G1-Region内指针]()
- (Bottom - Top): 已使用的空间;
- (Bottom - Prev TAMS): 已确定存活的对象;
- (Prev TAMS - Next TAMS): 在第 n-1 轮 Concurrent Marking 隐式存活的对象;
- (Next TAMS - Top): 在第 n 轮 Concurrent Marking 隐式存活的对象;
- (Top - End): 尚未使用,可分配的空间;
- (Bottom - Top): 已使用的空间;
- 并发标记中用到的两个 Bitmap
RS (Remembered Set): 解决并发标记阶段,Old -> Young 或 Young -> Old 跨代引用的问题;
- 每个 Region 附有个 RS, 用于记录其他 Region 中的对象引用本 Region 中对象的关系,Points-Into (谁引用了我)
CS (Collection Set): 一组可被回收的 Region 的集合;
TLAB (Thread Local Allocation Buffer): 本地线程缓冲区,为了使对象尽快分配到内存空间,每个线程都有固定的分区用于分配对象;
Humongous: 存放大对象的 Region (空间占 Region 一半及以上的对象)
- 在 Mixed GC 和 Full GC 回收
步骤:
- 初始化标记 (STW: 速度很快)
- 并发标记
- 最终标记 (STW)
- 清理 (STW)
# 初始化标记 (STW)
- 标记 GC Roots 对象 (从根集合可直接到达的对象);
- 将字段压入扫描栈 (marking stack), 等待后续的扫描;
- 使用外部 bitmap 记录扫 mark 信息 (未使用对象头中 mark word 里的 mark bit 来记录);
# 并发标记 (并发)
- 从扫描栈中取出对象引用,递归扫描整个堆获取的对象图;
- 对对象图上对象标记,压入扫描栈;
- 重复执行上两步,知道扫描栈为空;
- 扫描 SATB write barrier 所记录的引用 (扫描栈中执行完,就撤出);
===================
- 该阶段是并发进行的,就需要解决程序进行的过程中未标,漏标的情况. G1 通过 Writer barrier (Logging barrier) 来实现;
# 最终标记 (STW)
- 扫描并发标记后剩下的 SATB write barrier 所记录的引用;
- 扫描弱引用;
# 清理 (STW)
- 清除未标记的对象 (将 Region 中存活 (标记过) 的对象拷贝到空的 Region 中,回收原 Region);
- 可以自由选择任意多个 Region 来独立构成收集集合 CS (Collection Set)(靠 Per-Region Remembered Set 实现);
- 选择 CS 后,采用并行复制算法将 CS 中每个 Region 里存活的对象拷贝到新的 Region 中;
- 回收原 Region;
- 重置标记状态;
# 模式
- Young GC
- 当年轻代无法分配新对象时,选定所有年轻代里的 Region 进行收集清理 (可通过控制年轻代里的 Region 个数来控制 Young GC 的开销 (-XX:NewRatio=n))
- Mixed GC
- 视全局并发标记后统计,选定所有年轻代里的 Region 进行收集清理,根据 Global Concurrent Marking 统计得出收集收益高的若干老年代里的 Region, 在用户指定的开销目标范围内尽可能选择收益高的老年代里的 Region 进行收集清理;
- Global Concurrent Marking 主要是为 Mixed GC 提供服务,
- 视全局并发标记后统计,选定所有年轻代里的 Region 进行收集清理,根据 Global Concurrent Marking 统计得出收集收益高的若干老年代里的 Region, 在用户指定的开销目标范围内尽可能选择收益高的老年代里的 Region 进行收集清理;
==================
- Full GC (G1 GC 的控制范围内没有 Full GC)
- 若 Mixed GC 无法跟上程序分配内存的速度,导致老年代填满无法继续进行 Mixed GC, 就默认采用 Serial Old 算法 (和 -XX:+UseSerialGC 一样) 堆整个堆内存进行收集清理;
- Young GC 与 Mixed GC 之间视运行情况切换,虚拟机会定时做全局并发标记;
- 初始化标记默认在 Young GC 阶段执行;
- 全局并发标记进行时,虚拟机不会执行 Mixed GC;
- Mixed GC 进行时,虚拟机不能执行初始化标记;
- Tip: 正常流程中没有 Full GC, 老年代里的 Region 收集清理靠 Mixed GC 来完成.(视用户 Jvm 配置与程序运行情况而定);
# 使用方式
-XX:+UseG1GC // 开启使用G1垃圾收集器
-XX:G1HeapRegionSize=nm // 设置G1区域(Region)大小(范围 1 ~ 32MB), 堆为 2048 * Region (为减少过多分配Humongous对GC的影响, 特殊场景下可适当调整大小)
-XX:MaxGCPauseMillis=n // 期望GC最大的暂停时间, 默认: 200
-XX:ParallelGCThreads=n // GC过程中并行线程数, 默认: cpu <= 8 时, 8; >8时, 5/8 * cpu core
-XX:G1ConcRefinementThreads=n // 扫描log Buffer, 更新Rset时, 并发优化线程数
-XX:ConcGCThreads=n // 并发标记线程数, 默认: -XX:ParallelGCThreads 的1/4
-XX:+ParallelRefProcEnabled // 开启并行对象引用处理
-XX:ReferencesPerThread=n // 处理对象引用并行线程数, 最大线程数受 -XX:ParallelGCThreads 限制
-XX:NewRatio=n // 新老年代比例, 默认: 2 (1:2)
-XX:SurvivorRatio=n // Survivor与Eden比例, 默认: 8 (2:8)
-XX:G1NewSizePercent=n // 年轻代占堆的n%内存, 默认: 5
-XX:G1MaxNewSizePercent=n // 年轻代占堆的最大内存, 默认: 60
-XX:MaxTenuringThreshold=n // 对象在新生代经过n次YGC方可进入老年代, 默认: 15
-XX:InitiatingHeapOccupancyPercent=n // 触发标记的堆占用内存阈值(non_young_capacity_bytes: Old Generadion Region + Humongous), 默认: 45
-XX:G1HeapWastePercent=n // 愿意浪费堆的n%, Global Concurrent Marking后可回收内存小于n, 则不会触发Mixed GC, 默认: 5
-XX:G1ReservePercent=n // 预留G1HeapWastePercent堆的n%内存, 防止普升失败, 默认: 10
-XX:G1MixedGCLiveThresholdPercent=n // 一个Region中存活的对象超过n%, 就不会被认为可垃圾Region, 通过该参数可影响每次Mixed GC回收的Region数(n越大, 越容易被认为是垃圾分区), 默认: 85 [^1],[^2]
-XX:G1MixedGCCountTarget=n // 经过n次Mixed GC, 老年代所有Region将被回收一遍. 如果 Mixed GC中STW的时间过长, 可以考虑增大这个参数.(可用来调整每次Mixed GC回收多少个Region, 即有助于调节Mixed GC的停顿时间), 默认: 8 [^3]
-XX:G1RSetUpdatingPauseTimePercent=n // GC 清除阶段更新RSet的时间百分比, 默认: 10
-XX:+G1SummarizeRSetStats // 显示RS粗化过程
-XX:G1SummarizeRSetStatsPeriod=n // 每隔n次GC后收集一次RS的统计信息
-XX:+PrintAdaptiveSizePolicy // 打印"自适应调节策略"模式下运行的决策细节
-XX:+PrintReferenceGC // 打印详细的引用信息
# 优点
- 并行性:回收期间,可由多个 GC 线程同时工作,有效利用多核计算能力;
- 分代 GC: 同时兼顾年轻代与老年代;
- 空间整理:在每次回收时有效的复制对象,减少空间碎片;
- 停顿时间可控:在停顿时间上添加了预测机制,收集器将根据用户指定期望停顿时间回收收益率越高的 Region;
# 缺点
- Region 空间的大小与大对象占用空间很难保证一致,这将导致空间浪费 (Region 设置过小,分配超过一个 Region 空间的大对象时,有时寻找连续的空间会有麻烦);
# 总结
- 清理算法区别
- G1 的清理算法从根集合遍历对象图来判定对象的生死,不需要依赖 Global Concurrent Marking 的结果,有就用,没有也行;
- 标记 - 清除算法依赖于标记阶段对对象生死的判定;
# 引用
<<深入了解 Java 虚拟机 II>>
Concurrent Mark Sweep (CMS) Collector
CMS 垃圾收集器
Tomcat GC 参数详解
java 垃圾回收以及 jvm 参数调优概述
CMS 垃圾收集器详解
详解 CMS 垃圾回收机制
G1 论文
G1 - 名词
G1 - 算法原理
关于 incremental update 与 SATB 的一点理解
JAVA GARBAGE COLLECTION HANDBOOK
G1 垃圾收集器详解
垃圾优先型垃圾回收器调优 - 中文
垃圾优先型垃圾回收器调优 - 英文
Tips for Tuning the Garbage First Garbage Collector
徹底解剖「G1GC」実装編
G1GC 日志解析
G1GC 学习 - 官方
虚拟机收集器 - 官方
# 工具
- gceasy
- perfma
- plumbr
# 案例
- -XX:+CMSScavengeBeforeRemark
- -XX:SoftRefLRUPolicyMSPerMB
- -XX:G1MixedGCLiveThresholdPercent=n
# 常用参数
-verbose: gc 或者 -Xlog:gc
-XX:+PrintGCDateStamps // 打印GC的时间戳
-XX:+PrintGCDetails // 打印详细的GC日志
-XX:+PrintGCApplicationStoppedTime // 打印GC期间应用停止响应的时间
-XX:+PrintGCApplicationConcurrentTime // 答应GC期间应用运行时间
-XX:+UseGCLogFileRotation // 滚动输出GC日志
-XX:NumberOfGCLogFiles=10
-XX:GCLogFileSize=10M
-Xloggc: /home/XX/gc/XX_gc.log
-XX:NewRatio=n // 新老年代比例(默认: 2 (1:2))
-XX:SurvivorRatio=n // Survivor与Eden比例(默认: 8 (2:8))
-XX:MaxTenuringThreshold=n // 对象在新生代经过n次YGC方可进入老年代(默认: 15)
-XX:+UnlockDiagnosticVMOptions //后面2个参数的需要
-XX:+PrintCompilation //打印JIT编译详情
-XX:+PrintInlining //打印内联详情
// OOM输出
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/home/XX/dump_OOME.hprof
-XX:+DisableExplicitGC
// 收集器
-XX:+UseSerialGC // 串行收集器
-XX:+UseParallelGC // 并行收集器
-XX:+UseParalledlOldGC // 并行老年代收集器
-XX:+UseConcMarkSweepGC // 并发收集器
