parNew垃圾收集器

104 阅读2分钟

parNew垃圾收集器

ParNew 收集器是 JVM 中针对年轻代多线程并行收集器,作为 CMS 收集器的黄金搭档,在多核时代扮演着重要角色。ParNew与serial实现基本一致,只是在停顿收集时,是多线程收集。以下是全面剖析:

graph TD
    A[ParNew 收集器] --> B[核心特性]
    A --> C[工作模式]
    A --> D[内存管理]
    A --> E[性能特征]
    A --> F[调优策略]

    B --> B1[多线程并行]
    B --> B2[STW 暂停]
    B --> B3[复制算法]

    C --> C1[多线程标记-复制]
    C --> C2[与CMS协同]

    D --> D1[分代模型]
    D --> D2[卡表技术]

    E --> E1[多核优势]
    E --> E2[暂停时间]

    F --> F1[线程数优化]
    F --> F2[空间分配]

一、核心设计定位

1. 历史地位与定位

timeline
    title ParNew发展史
    section JDK 1.4.2
      “ 首次引入 ” : 作为Serial的多线程版本
    section JDK 5
      “ 成为CMS标配 ” : 年轻代唯一选择
    section JDK 9
      “ 被G1取代 ” : 不再是默认选项
    section JDK 16
      “ 仍可启用 ” : 特殊场景使用

2. 架构定位

graph LR
    CMS[老年代CMS] --> ParNew[年轻代ParNew]
    ParNew --> 优势[完美协同]

    优势 --> 停顿协调[GC停顿时间协调]
    优势 --> 内存管理[分代策略统一]
    优势 --> 卡表共享[共享跨代引用信息]

二、工作模式详解

1. 完整收集流程

sequenceDiagram
    App->>JVM: 对象分配请求
    JVM->>ParNew: Eden空间不足
    ParNew->>所有线程: STW暂停(初始停顿)
    loop 多线程并行
        ParNew->>ParNew: 标记存活对象
        ParNew->>ParNew: 复制到Survivor区
    end
    ParNew->>所有线程: 恢复运行

2. 多线程并行机制

graph TD
    任务划分 --> 分区[堆内存分区]
    分区 --> 线程[线程独立工作]

    线程 --> 负载均衡[自动负载均衡]
    负载均衡 --> 策略[任务窃取机制]

    同步 --> 屏障[GC屏障]
    屏障 --> 同步点[全局同步点]

三、内存管理机制

1. 分代结构

graph TD
    Heap[堆内存] --> Young[年轻代]
    Heap --> Old[老年代]

    Young --> Eden[Eden区]
    Young --> S0[Survivor0]
    Young --> S1[Survivor1]

    Old --> Tenured[老年代空间]

2. 卡表技术(Card Table)

graph LR
    跨代引用 --> 卡表[Card Table]
    卡表 --> 字节数组[512字节/卡页]

    写屏障 --> 标记[标记脏卡]
    标记 --> MinorGC[Minor GC时扫描]

四、性能特征分析

1. 多核加速效果

graph LR
    线程数 --> 加速比[性能提升]

    单核 --> 基准1x
    双核 --> 1.8x
    四核 --> 3.2x
    八核 --> 5.5x

2. 暂停时间模型

graph TD
    暂停时间 --> 因素
    因素 --> 堆大小[年轻代大小]
    因素 --> 存活率[对象存活率]
    因素 --> 线程数[GC线程数]

    优化 --> 公式["Tpause = (Syoung * Rlive) / (Ncore * Vcopy)"]

五、关键配置参数

1. 基础启用参数

# 启用ParNew(需配合CMS)
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC

2. 核心调优参数

参数默认值建议值作用
-XX:ParallelGCThreadsCPU核数等于物理核数GC线程数
-XX:SurvivorRatio86-10Eden/Survivor比例
-XX:MaxTenuringThreshold1510-15晋升阈值
-XX:TargetSurvivorRatio5060-80Survivor目标利用率

六、最佳实践配置

1. 标准配置模板

# 4核服务器配置
java -XX:+UseConcMarkSweepGC \
     -XX:+UseParNewGC \
     -XX:ParallelGCThreads=4 \
     -XX:SurvivorRatio=8 \
     -XX:MaxTenuringThreshold=10 \
     -Xms4g -Xmx4g \
     -jar application.jar

2. 性能优化方向

mindmap
  root((优化方向))
    减少暂停时间
      减小年轻代(-Xmn)
      降低存活率(对象池)
    提高吞吐量
      增加GC线程
      增大Eden区
    避免晋升
      增大Survivor
      调整晋升阈值

七、与其它收集器对比

1. 年轻代收集器对比

特性ParNewParallel ScavengeG1 Young
算法复制复制复制
线程并行并行并行
目标低暂停高吞吐可预测暂停
协同CMS专属独立G1统一管理
适用中小堆大堆超大堆

2. 暂停时间对比(4核/2GB堆)

收集器平均暂停最大暂停吞吐量
Serial120ms250ms中等
ParNew40ms80ms
G120ms50ms中高

八、现代演进与替代

1. JDK 8+ 的替代方案

graph TD
    ParNew --> 局限[年轻代专用]
    局限 --> 替代[G1/ZGC统一收集]

    替代优势 --> 统一管理[无代际协同问题]
    替代优势 --> 算法优化[更先进算法]
    替代优势 --> 大堆支持[>32GB堆]

2. 迁移建议

flowchart TD
    评估[评估当前系统] --> 特征{特征分析}
    特征 -->|暂停敏感| G1[迁移到G1]
    特征 -->|大堆>32GB| ZGC[迁移到ZGC]
    特征 -->|CMS稳定运行| 保持[保持ParNew+CMS]

九、生产环境案例

1. 电商平台优化案例

graph TD
    问题[高峰期GC暂停200ms] --> 分析[诊断工具分析]
    分析 --> 原因[年轻代过大]
    优化 --> 调整[-Xmn从2G调为1G]
    优化 --> 线程[-XX:ParallelGCThreads=16]
    结果 --> 提升[暂停降至50ms]

2. 参数调整效果验证

# 调整前
jstat -gcutil <pid> 1000
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    GCT
  0.00  50.00  90.00  60.00  95.00  90.00    100   40.000     5    2.000  42.000

# 调整后
  0.00  50.00  80.00  55.00  95.00  90.00    150   30.000     5    2.000  32.000

十、总结结论

  1. 核心价值

    • CMS 收集器的最佳拍档
    • 多核环境下年轻代并行收集最优解
    • 低暂停时间的经典实现
  2. 适用场景

    pie
        title ParNew适用场景
        "4-8核服务器" : 45
        "堆<32GB"  : 35
        "JDK8及以下"  : 20
    
  3. 迁移建议

    • JDK 8 及以下:继续使用 ParNew+CMS
    • JDK 11+:迁移到 G1 或 ZGC
    • 新项目:直接采用 ZGC/Shenandoah

​最终配置建议​​:

在 JDK 8 环境中,对于 4-16 核服务器和 4-32GB 堆内存的应用,采用 -XX:+UseConcMarkSweepGC -XX:ParallelGCThreads=N(N=CPU核数)是最佳实践。但对于新项目,建议直接基于 JDK 17+ 使用 ZGC。