• JVM常见的垃圾回收器(详细)


    1、Young为年轻代出发的垃圾回收器
    2、Old为老触发的垃圾回收器。
    3、连线代表的是垃圾回收器的组合。CMS 和Serial Old连线代表CMS一旦不行了,Serial Old上场。

    在这里插入图片描述

    首先了解一个概念:STW

    1、什么是STW?
    STW是Stop-The-World缩写: 是在垃圾回收算法执⾏过程当中,将JVM内存冻结丶应用程序停顿的⼀种状态。

    1、在STW 状态下,JAVA的所有线程都是停⽌执⾏的 -> GC线程除外
    2、一旦Stop-the-world发生,除了GC所需的线程外,其他线程都将停止工作,中断了的线程直到GC任务结束才继续它们的任务。
    3、STW是不可避免的,垃圾回收算法执⾏一定会出现STW,我们要做的只是减少停顿的时间
    GC各种算法优化的重点,就是减少STW(暂停),同时这也是JVM调优的重点。

    2、什么时候进入STW状态?
    可达性分析算法中枚举根节点(GC Roots)会导致所有Java执行线程停顿,进入STW状态

    3、为什么一定要STW停顿的原因?
    1、分析工作必须在一个能确保一致性的快照中进行
    2、一致性指整个分析期间整个执行系统看起来像被冻结在某个时间点上
    3、如果出现分析过程中对象引用关系还在不断变化,则分析结果的准确性无法保证
    4、被STW中断的应用程序线程会在完成GC之后恢复,频繁的中断会让用户感觉卡顿
    5、所以我们要减少STW的发生,也就相当于要想办法降低GC垃圾回收的频率
    6、STW状态和采用哪款GC收集器无关,所有的GC收集器都有这个状态,因为要保证一致性。
    7、但是好的GC收集器可以减少停顿的时间、减少STW(暂停)和降低GC垃圾回收的频率是调优的重点

    如果系统卡顿很明显,大概率就是频繁执行GC垃圾回收,频繁进入STW状态产生停顿的缘故

    记住,目前所有垃圾收集器都会进入STW机制

    一、Serial收集器(单线程收集器,新时代,采用复制算法)

    这个收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,会进行SWT,停掉除了GC线程之外的所有的工作线程,直到它收集结束,才继续执行其它的工作线程。

    例:当一个小孩在房间玩,乱丢垃圾,这时他妈妈进来收拾垃圾了,会把小孩抱到一边,不让他玩先,等收拾好垃圾,在让他玩。

    在这里插入图片描述

    二、Serial Old收集器(单线程收集器,老年代,采用标记整理算法)

    Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器。会进行SWT,停掉除了GC线程之外的所有的工作线程,直到它收集结束,才继续执行其它的工作线程。
    在这里插入图片描述

    三、Parallel Scavenge收集器(多线程收集器,新生代、采用复制算法)

    Parallel Scavenge 收集器也是一款新生代收集器,它同样是基于标记 -复制算法实现的收集器,是 能够并行收集的多线程收集器。

    Parallel Scavenge收集器的特点是它的关注点与其他收集器不同。Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值,即:
    在这里插入图片描述
    举例说明:
    如果虚拟机完成某个任务,用户代码加上垃圾收集总共耗费了 100 分钟,其中垃圾收集花掉 1 分
    钟,那吞吐量就是 99% 。
    Parallel Scavenge收集器也经常被称作“吞吐量优先收集器”。

    如果在上线之前没有做任何JVM调优或设置话,默认的就是Parallel Scavenge + Paraller Old组合垃圾回收器,简称PS + PO

    在这里插入图片描述

    四、Paraller Old收集器(多线程收集器,老年代,采用标记整理算法)

    Parallel Old是Parallel Scavenge收集器的老年代版本
    在这里插入图片描述

    五、ParNew收集器(多线程收集器,新时代,采用标记-整理算法)

    ParNew说白了和Parallel Scavenge一样的,区别组了做了增强,以便能让它和CMS配合使用。

    在这里插入图片描述

    六、CMS收集器(多线程收集器,老年代,采用标记—清除算法)

    CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器

    从名字(包含“Mark Sweep”)上就可以看出,CMS收集器是基于“标记—清除”算法实现的,它的运作过程相对于前面几种收集器来说更复杂一些,整个过程分为4个步骤,包括:

    初始标记(CMS initial mark)
    并发标记(CMS concurrent mark)
    重新标记(CMS remark)
    并发清除(CMS concurrent sweep)

    ParNew
    在这里插入图片描述

    1、初始标记(触发STW):初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。

    2、并发标记(不触发STW):并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程, 这个过程耗时较长但是不需要停顿用户线程, 可以与垃圾收集线程一起并发运行。因为用户程序继续运行,可能会有导致已经标记过的对象状态发生改变。所以要进行一次重新标记。

    3、重新标记(触发STW):重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,就是并发标记过程中产生的新垃圾,进行标记,或者到重新标记这一阶段,又要用到并发标记已经标记的垃圾,取消垃圾标记。 这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。

    4、并发清理(不触发STW) :开启用户线程,同时GC线程开始对未标记的区域做清扫。这个阶段如果有新增对象会被标记为黑色不做任何处理,等下次重新触发在进行处理。

    CMS问题:
    在这里插入图片描述

    七、G1(多线程收集器,标记-整理)

    在这里插入图片描述

    1、G1 垃圾收集器的内存模型

    G1 收集器不采用传统的新生代和老年代物理隔离的布局方式,仅在逻辑上划分新生代和老年代,将整个堆内存划分为2048个大小相等的独立内存块Region,每个Region是逻辑连续的一段内存,具体大小根据堆的实际大小而定,整体被控制在 1M - 32M 之间,且为2的N次幂(1M、2M、4M、8M、16M和32M),并使用不同的Region来表示新生代和老年代,G1不再要求相同类型的 Region 在物理内存上相邻,而是通过Region的动态分配方式实现逻辑上的连续。

    G1收集器通过跟踪Region中的垃圾堆积情况,每次根据设置的垃圾回收时间,回收优先级最高的区域,避免整个新生代或整个老年代的垃圾回收,使得stop the world的时间更短、更可控,同时在有限的时间内可以获得最高的回收效率。

    通过区域划分和优先级区域回收机制,确保G1收集器可以在有限时间获得最高的垃圾收集效率。

    2、分区Region:
    G1 垃圾收集器将堆内存划分为若干个 Region,每个 Region 分区只能是一种角色,Eden区、S区、老年代区的其中一个,空白区域代表的是未分配的内存,最后还有个特殊的区域H(Humongous),专门用于存放巨型对象,如果一个对象的大小超过Region容量的50%以上,G1 就认为这是个巨型对象。在其他垃圾收集器中,这些巨型对象默认会被分配在老年代,但如果它是一个短期存活的巨型对象,放入老年代就会对垃圾收集器造成负面影响,触发老年代频繁GC。

    为了解决这个问题,G1划分了一个H区专门存放巨型对象,如果一个H区装不下巨型对象,那么G1会寻找连续的H分区来存储,如果寻找不到连续的H区的话,就不得不启动 Full GC 了。
    在这里插入图片描述
    3、card table

    卡表是 CMS 的解决方案。

    官方解释:A kind of remembered set that records where oops have changed in a generation.

    问题: 新生代回收为什么可以不全表扫描标记?
    答案: 只标记在新生代区的根引用+ cardTable上的标记。
    作用:采用空间换时间,不需要扫描整个Heap空间,降低MonitorGC耗时。

    在这里插入图片描述
    4、Remember Set:

    在这里插入图片描述

    在串行和并行收集器中,GC时是通过整堆扫描来确定对象是否处于可达路径中。然而G1为了避免STW式的整堆扫描,为每个分区各自分配了一个 RSet(Remembered Set),它内部类似于一个反向指针,记录了其它 Region 对当前 Region 的引用情况,这样就带来一个极大的好处:回收某个Region时,不需要执行全堆扫描,只需扫描它的 RSet 就可以找到外部引用,来确定引用本分区内的对象是否存活,进而确定本分区内的对象存活情况,而这些引用就是 initial mark 的根之一。

    事实上,并非所有的引用都需要记录在RSet中,如果引用源是本分区的对象,那么就不需要记录在 RSet 中;同时 G1 每次 GC 时,所有的新生代都会被扫描,因此引用源是年轻代的对象,也不需要在RSet中记录;所以最终只需要记录老年代到新生代之间的引用即可。

    5、Mixed GC:

    在这里插入图片描述

    年轻代不断进行垃圾回收活动后,为了避免老年代的空间被耗尽。当占用空间超过整堆比 IHOP 阈值 -XX:InitiatingHeapOccupancyPercent(默认45%)时,G1就会启动一次混合垃圾回收Mixed GC,Mixed GC不仅进行正常的新生代垃圾收集,同时也回收部分后台扫描线程标记的老年代分区。Mixed GC步骤主要分为两步:

    这里需要特别注意的是 Mixed GC 并不是 Full GC,只有当 Mixed GC 来不及回收old region,也就说在需要分配老年代的对象时,但发现没有足够的空间,这个时候就会触发一次 Full GC

    在这里插入图片描述
    6、并发标记算法

    三色标记算法:
    三色标记算法是并发收集阶段的重要算法,它是描述追踪式回收器的一种有用的方法,利用它可以推演回收器的正确性。 首先,我们将对象分成三种类型的。
    在这里插入图片描述
    三色标记算法的正常流程:
    (1)根对象被置为黑色,子对象被置为灰色:
    在这里插入图片描述
    (2)继续由灰色遍历,将已扫描了子对象的对象置为黑色。
    在这里插入图片描述

    (3)遍历了所有可达的对象后,所有可达的对象都变成了黑色。不可达的对象即为白色,需要被清理。
    在这里插入图片描述

    漏标:
    在并发标记过程中,黑色对象指向了白色对象,以此同时灰色对象取消了对白色对象的引用,就会造成漏标,因为A是黑色对象,不需要在扫描了,B又取消了对D的引用,从而产生漏标
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    > G1使用的是SATB防止漏标

    八、GC常用的参数

    GC常用参数

    • -Xmn -Xms -Xmx -Xss
      年轻代 最小堆 最大堆 栈空间
    • -XX:+UseTLAB
      使用TLAB,默认打开
    • -XX:+PrintTLAB
      打印TLAB的使用情况
    • -XX:TLABSize
      设置TLAB大小
    • -XX:+DisableExplictGC
      System.gc()不管用 ,FGC
    • -XX:+PrintGC
    • -XX:+PrintGCDetails
    • -XX:+PrintHeapAtGC
    • -XX:+PrintGCTimeStamps
    • -XX:+PrintGCApplicationConcurrentTime (低)
      打印应用程序时间
    • -XX:+PrintGCApplicationStoppedTime (低)
      打印暂停时长
    • -XX:+PrintReferenceGC (重要性低)
      记录回收了多少种不同引用类型的引用
    • -verbose:class
      类加载详细过程
    • -XX:+PrintVMOptions
    • -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial
      必须会用
    • -Xloggc:opt/log/gc.log
    • -XX:MaxTenuringThreshold
      升代年龄,最大值15
    • 锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 …
      这些不建议设置

    Parallel常用参数

    • -XX:SurvivorRatio
    • -XX:PreTenureSizeThreshold
      大对象到底多大
    • -XX:MaxTenuringThreshold
    • -XX:+ParallelGCThreads
      并行收集器的线程数,同样适用于CMS,一般设为和CPU核数相同
    • -XX:+UseAdaptiveSizePolicy
      自动选择各区大小比例

    CMS常用参数

    • -XX:+UseConcMarkSweepGC
    • -XX:ParallelCMSThreads
      CMS线程数量
    • -XX:CMSInitiatingOccupancyFraction
      使用多少比例的老年代后开始CMS收集,默认是68%(近似值),如果频繁发生SerialOld卡顿,应该调小,(频繁CMS回收)
    • -XX:+UseCMSCompactAtFullCollection
      在FGC时进行压缩
    • -XX:CMSFullGCsBeforeCompaction
      多少次FGC之后进行压缩
    • -XX:+CMSClassUnloadingEnabled
    • -XX:CMSInitiatingPermOccupancyFraction
      达到什么比例时进行Perm回收
    • GCTimeRatio
      设置GC时间占用程序运行时间的百分比
    • -XX:MaxGCPauseMillis
      停顿时间,是一个建议时间,GC会尝试用各种手段达到这个时间,比如减小年轻代

    G1常用参数

    • -XX:+UseG1GC
    • -XX:MaxGCPauseMillis
      建议值,G1会尝试调整Young区的块数来达到这个值
    • -XX:GCPauseIntervalMillis
      ?GC的间隔时间
    • -XX:+G1HeapRegionSize
      分区大小,建议逐渐增大该值,1 2 4 8 16 32。
      随着size增加,垃圾的存活时间更长,GC间隔更长,但每次GC的时间也会更长
      ZGC做了改进(动态区块大小)
    • G1NewSizePercent
      新生代最小比例,默认为5%
    • G1MaxNewSizePercent
      新生代最大比例,默认为60%
    • GCTimeRatio
      GC时间建议比例,G1会根据这个值调整堆空间
    • ConcGCThreads
      线程数量
    • InitiatingHeapOccupancyPercent
      启动G1的堆空间占用比例
  • 相关阅读:
    centos中nacos设置开机自启动
    C++ - 类型转换 - static_cast - reinterpret_cast - const_cast - dynamic_cast
    适配器模式 结构性模式之五
    快速上手 TypeScript
    Java之final和abstract关键字(9)
    02-CSS3基本样式
    认识进制
    arthas the number of matched classs is 65
    支持内录系统声音的Mac录屏软件Omi Recorder
    洛谷 P1281 书的复制(二分答案 输出方案)
  • 原文地址:https://blog.csdn.net/javaeEEse/article/details/134033663