• 系统在运行的过程中,会创建许许多多的对象,这些对象会经历生老死的历程,而那些’死’的对象需要进行回收为新生对象提供空间.

# 前言

  • 系统在运行的过程中,会创建许许多多的对象,这些对象会经历生老死的历程,而那些’死’的对象需要进行回收为新生对象提供空间.

# 回收算法

# 引用计数算法

# 描述
  • 每个对象均配有一个引用计数器,当对象被引用一次,计数器 + 1, 反之移除对该对象的引用时,计数器 - 1, 当值为 0 时,将被 GC 列为回收对象.
# 优点
  • 算法简单
# 缺点
  • 当出现循环依赖时,造成彼此间计数器不为 0, 则不能被 GC 回收,因此产生内存泄漏问题
# 例子
# 代码
a
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 节点没有任何引用链相连接时,则认为该对象是不可用的 (即使该对象还与其他对象相关联).
# 例子
# 代码
a
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;
    }
}
# 输出
g
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 () 俗称:自救), 对象在该过程期间是否与引用链上的任何一个对象建立关联:
      • 有,从回收集合中移除;
      • 没有,进行回收
    • 没有必要,加入回收集合;
# 使用 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 来实现虚引用,对象是否有虚引用的存在,完全不会对其生存时间构成影响,无法通过虚引用来取得一个对象实例;
  • 作用:可以在垃圾收集器回收时收到一个通知;

# 垃圾回收算法

# 标记 - 清除算法

# 描述
  1. 标记存活的对象;
  2. 统一回收未标记的对象;

标记-清除算法

# 缺点
  • 效率问题 (执行期间会 Stop The World)
  • 空间问题 (容易产生大量不连续的空间碎片,无法分配大对象,会导致频繁 Minjor GC, 以至导致 Full GC, 最终 OOM)
# 总结
  • 适用于单核机器上;

# 标记 - 整理算法

# 描述
  • 该算法是对’标记 - 清除算法’的优化;
  1. 标记存活对象;
  2. 存活对象向一端一端,清理存活边界以外内存;

标记-整理算法

# 优点
  • 不会产生内存碎片;
# 总结
  • 该算法适用于对象’存活率高’的区域;
  • 适用于老年代区;

# 标记 - 复制算法

# 描述
  1. 标记存活对象;
  2. 将存活对象复制到另外一半内存;
  3. 清除当前这一半的内存区域;

标记-复制算法

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

# 垃圾收集器

# Serial 收集器

垃圾收集器-Serial

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

# ParNew 收集器

垃圾收集器-Serial

# 描述
  • 基于’标记 - 复制算法’ ' 并行’的多线程收集器 (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) 收集器

垃圾收集器-CMS

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

# 初始化标记 (STW)

垃圾收集器-CMS-初始化标记

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

垃圾收集器-CMS-并发标记

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

垃圾收集器-CMS-应用程序操作导致

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

垃圾收集器-CMS-预清理02

  • 处理并发标记阶段 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 收集器

垃圾收集器-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): 已使用的空间;
        1. (Bottom - Prev TAMS): 已确定存活的对象;
        2. (Prev TAMS - Next TAMS): 在第 n-1 轮 Concurrent Marking 隐式存活的对象;
        3. (Next TAMS - Top): 在第 n 轮 Concurrent Marking 隐式存活的对象;
      • (Top - End): 尚未使用,可分配的空间;
  • 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 回收
  • 步骤:

    1. 初始化标记 (STW: 速度很快)
    2. 并发标记
    3. 最终标记 (STW)
    4. 清理 (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 提供服务,

==================

  • 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 // 并发收集器

# 标注

Edited on Views times