五、垃圾回收器

一、串行搜集器在这里插入图片描述

  • Serial收集器

    • 同一时间只有一个CPU执行垃圾回收,此时应用程序暂停,直到垃圾回收完成;
    • 最古老的搜集器,最稳定;
    • 单线程没有线程间交互,回收效率高;
    • 当回收的内存很大时会造成长时间停顿;
    • 新生代使用复制算法、老年代使用标记-压缩算法;
    • 配置:-XX:+UseSerialGC,新生代使用Serial、老年代使用Serial Old,日志如下;
      在这里插入图片描述

二、并行搜集器

  • ParNew收集器在这里插入图片描述

    • ParNew是Serial的多线程版本;
    • 新生代垃圾收集器,回收算法、策略、控制参数与Serial完全一致;
    • 使用复制算法;
    • 除Serial以外,唯一可以和CMS配合使用的;
    • -XX:ParallelGCThreads=n:可用于垃圾回收的最大线程数;
    • 配置:-XX:+UseParNewGC,年轻代使用ParNew、老年代使用Serial Old(这个组合JVM官方不推荐,后续可能会移除),日志如下:在这里插入图片描述
  • Parallel Scavenge收集器(吞吐量优先收集器)在这里插入图片描述

    • 新生代收集器、采用复制算法、并行;
    • 与其他收集器相比更关注吞吐量:吞吐量 = 工作线程所占时间 / (工作线程所占时间 + 垃圾回收所占时间);
    • 自适应调节:虚拟机会收集运行性能信息,动态调整垃圾回收停顿时间,和最大吞吐量;
    • -XX:MaxGCPauseMills=n:垃圾回收最大停顿时间(毫秒),设置之后虚拟机会尽量保证回收时间不超过设定值;
    • -XX:GCTimeRatio=n:0~100整数,表示期望GC的时间不超过程序运行时间的1/(1+n),默认99;
    • -XX:+UseParallelGC或-XX:+UseParallelOldGC可相互激活;在这里插入图片描述
  • Parallel Old收集器

    • Parallel Scavenge 的老年代版本;
    • 与Parallel Scavenge配合使用。

三、并发收集器

  • CMS收集器(Concurrent Mark Sweep:并发标记清除)在这里插入图片描述

    • 关注回收时间,期望获取最短回收时间;
    • 老年代收集器,年轻代使用ParNew,标记清除算法,并发回收垃圾,会造成内存碎片,且无法完全清理垃圾(并发时工作线程也在产生垃圾);
    • 由于并发,GC时工作线程也在工作,所以不能整理内存空间(改变对象存储位置),只能使用标记清除算法;
    • 使用串行收集器做后备(当剩余内存不足以支撑工作线程并发运行时,引起concurrent mode failure并使用串行收集器执行一次GC);
    • 回收过程:
      • 初始标记:标记根节点直接关联到的对象,速度快;
      • 并发标记:便利标记所有存活对象,耗时较长,GC线程与工作线程并发工作,无需停顿,但会降低吞吐量,是程序变慢;
      • 重新标记:修正并发标记中由于工作线程同时工作产生的对象变更,该过程比初始标记耗时长一点,但是远小于并发标记耗时;
      • 并发清理:清理死亡对象,无需整理空间,所以该阶段也可与工作线程并发运行。
    • -XX:CMSInitiatingOccupancyFraction=70:设置触发GC的阈值,老年代使用率超过70%执行GC;
    • -XX:CMSFullGCsBeforeCompaction=2:设置进行2次FullGC之后进行碎片整理;
    • -XX:ParallelCMSThreads:设置CMS的线程数量;
    • -XX:+UseConcMarkSweepGC;
/**
 * @author xiaomu
 * @title: JVMTest
 * @description: JVM测试类
 * @date 2022/8/1014:15
 */
public class JVMTest extends ClassLoader{
    public static final int _1MB = 1024 * 1024;
    public static void main(String[] args) {
        byte[] b1 = new byte[4 * _1MB];
        System.out.println("----111----");
        byte[] b2 = new byte[4 * _1MB];
        System.out.println("----222----");
        byte[] b3 = new byte[4 * _1MB];
        System.out.println("----333----");
        byte[] b4 = new byte[2 * _1MB];
        System.out.println("----444----");
    }
}

在这里插入图片描述

  • G1搜集器

    • 内存划分:在这里插入图片描述
      • G1也使用分代的思想,但不再将年轻代、老年代分为固定大小的区域,而是将连续的java堆分为约2048个大小相等的分区(Region),每个Region根据需要扮演eden、survivor、old区域(每个Region同一时间只能扮演一个角色)
      • 使用特殊的Region:Humongous存储大对象(查过一个Region内存一半的为大对象),当一个对象超过一个Region内存,则使用连续的Region存储,Humongous区作为老年代看待;
      • 同一区的Region不需要连续;
    • 回收算法:
      • 停顿时间模型:G1尽量避免回收全区域,每次回收整数个Region,因此可预测回收所需停顿时间;
      • 价值:回收时间少,回收内存多价值越高;
      • G1会根据Region的价值大小在后台维护一个列表,每次根据设定的停顿时间,优先处理价值大的区域;
      • 整体使用标记整理算法,局部使用标记复制算法。
    • 回收过程:
      • 初始标记:仅标记根节点直接关联对象,并修改TAMS指针值,使下一阶段工作线程并发时可以正常分配内存,程序停顿,耗时短;
      • 并发标记:扫描全堆,对所有对象进行可达性分析,完成之后会重新处理STAB在并发时有引用变动的对象,与用户线程并发,耗时长;
      • 最终标记:处理表发标记留下的少了STAB记录,程序停顿,耗时短;
      • 筛选回收:更新Region统计数据,对Region通过成本(耗时)和价值排序,根据用户期望的停顿时间选择任意多个Region,将选择回收的Region中存活对象移到空Region中,清空就Region,涉及存活对象移动,需暂停工作线程;
    • 参数
      • -XX:+UseG1GC:使用G1;
      • -XX:G1HeapRegionSize:设置 Region 大小(范围 1~32M,且为 2 的 N 次幂);
      • -XX:MaxGCPauseMillis:最大收集停顿时间(默认 200 毫秒)。

四、总结

  • Serial:串行、单线程、最稳定、最古老,适用于单核,或对停顿时间没要求;
  • ParNew:并行,年轻代收集器,CMS默认搭配的年轻代收集器;
  • Parallel:并行,jdk1.8默认使用的收集器,注重吞吐量,适用于多核,服务器资源稀缺;
  • CMS:并发,存在内存碎片,且无法完全清理,适用于程序需要及时响应;
  • G1:可控的停顿时间,着手解决吞吐量与停顿时间间的矛盾,期望在可控的停顿时间内最大限度的提高吞吐量,jdk9之后默认使用G1。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值