Java虚拟机-垃圾回收器

简介: Java虚拟机-垃圾回收器

 image.gif编辑

一、如何查看垃圾回收器

查看JVM的默认垃圾回收器,可以看出JDK8中UseParallelGC即Parallel Scavenge+Serial Old

java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=266359616 
-XX:MaxHeapSize=4261753856 
-XX:+PrintCommandLineFlags 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:-UseLargePagesIndividualAllocation 
-XX:+UseParallelGC
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)

image.gif

查看垃圾回收器详细信息

java -XX:+PrintGCDetails -version
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)
Heap
 PSYoungGen      total 76288K, used 3932K [0x000000076b500000, 0x0000000770a00000, 0x00000007c0000000)
  eden space 65536K, 6% used [0x000000076b500000,0x000000076b8d7240,0x000000076f500000)
  from space 10752K, 0% used [0x000000076ff80000,0x000000076ff80000,0x0000000770a00000)
  to   space 10752K, 0% used [0x000000076f500000,0x000000076f500000,0x000000076ff80000)
 ParOldGen       total 175104K, used 0K [0x00000006c1e00000, 0x00000006cc900000, 0x000000076b500000)
  object space 175104K, 0% used [0x00000006c1e00000,0x00000006c1e00000,0x00000006cc900000)
 Metaspace       used 2366K, capacity 4480K, committed 4480K, reserved 1056768K
  class space    used 255K, capacity 384K, committed 384K, reserved 1048576K

image.gif

二、常见垃圾回收器介绍

1.Serial

使用场景主要为客户端。是最基本,发展最悠久的单线程垃圾收集器,是JDK1.3.1前HotSpot新生代收集的唯一选择。分为新生代和老年代两个,分别采用了复制算法和标记-整理算法。

image.gif编辑

1)特点

    • 针对新生代;
    • 采用复制算法;
    • 单线程收集;
    • 进行垃圾收集时,必须暂停所有工作线程,直到完成,即会"Stop The World"。

    2)应用场景

      • 依然是HotSpot在Client模式下默认的新生代收集器;
      • 简单高效(与其他收集器的单线程相比);
      • 对于限定单核CPU的环境,没有线程开销,可以获得最高的单线程收集效率;
      • 在用户的桌面应用场景中,可用内存一般不大(几十M至一两百M),可以在较短时间内完成垃圾收集(几十MS至一百多MS),只要不频繁发生,可以接受。

      3)使用方式

      "-XX:+UseSerialGC":添加该参数来显式使用串行垃圾收集器

      2.ParNew

      ParNew垃圾收集器是Serial收集器的多线程版本。

      image.gif编辑

      1)特点

        • 除了多线程外,其余的行为、特点和Serial收集器一样,如Serial收集器可用控制参数、收集算法、Stop The World、内存分配规则、回收策略等;
        • 两个收集器共用了不少代码。

        2)应用场景

          • 在Server模式下,ParNew收集器是一个非常重要的收集器,因为除Serial外,目前只有它能与CMS收集器配合工作;
          • 但在单个CPU环境中,不会比Serail收集器有更好的效果,因为存在线程交互开销。

          3)使用方式

          -XX:+UseConcMarkSweepGC:指定使用CMS后,会默认使用ParNew作为新生代收集器;

          -XX:+UseParNewGC:强制指定使用ParNew;

          -XX:ParallelGCThreads:指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同;

          3.Parallel

          Parallel Scavenge 收集器是一个新生代收集器,采用复制算法,并且是多线程收集器;与ParNew最大的不同,它关注的是垃圾回收的吞吐量(Throughput)。

          Parallel Old 收集器是Parallel Scavenge 收集器的老年代版本,使用多线程和“标记-整理”算法。

          JDK7和8环境下,默认使用 Parallel Scavenge(新生代)+ Serial Old(老年代)。

          image.gif编辑

          1)特点

            • 在注重吞吐量以及CPU资源敏感的场合,可以优先考虑Parallel Scavenge 加 Parallel Old 收集器。
            • 吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)

            2)应用场景

              • 主要适合在后台运算而不需要太多交互的任务:
              • 停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效的利用CPU时间,尽快完成程序的运算任务。

              3)使用方式

              -XX:+UseParallelGC :使用Parallel作为垃圾收集器;

              -XX:MaxGCPauseMillis:控制最大垃圾收集停顿时间

              -XX:GCTimeRatio:设置吞吐量大小

              4.CMS

              CMS,Concurrent Mark Sweep,同样是老年代的收集器。它关注的是垃圾回收最短的停顿时间(低停顿),在老年代并不频繁GC的场景下,是比较适用的。

              1)整体流程

                • 初始标记(initial mark),单线程执行,需要“Stop The World”,但仅仅把GC Roots的直接关联可达的对象给标记一下,由于直接关联对象比较小,所以这里的速度非常快。
                • 并发标记(concurrent mark),对于初始标记过程所标记的初始标记对象,进行并发追踪标记,此时其他线程仍可以继续工作。此处时间较长,但不停顿。
                • 重新标记(remark),在并发标记的过程中,由于可能还会产生新的垃圾,所以此时需要重新标记新产生的垃圾。此处执行并行标记,与用户线程不并发,所以依然是“Stop The World”,时间比初始时间要长一点。
                • 并发清除(concurrent sweep),并发清除之前所标记的垃圾。其他用户线程仍可以工作,不需要停顿。

                image.gif编辑

                2)特点

                  • 由于最耗费时间的并发标记与并发清除阶段都不需要暂停工作,所以整体的回收是低停顿的;
                  • Mark Sweep算法会导致内存碎片比较多;
                  • CMS的并发能力依赖于CPU资源,所以在CPU数少和CPU资源紧张的情况下,性能较差;
                  • 并发清除阶段,用户线程依然在运行,所以依然会产生新的垃圾,此阶段的垃圾并不会再本次GC中回收,而放到下次。所以GC不能等待内存耗尽的时候才进行GC,这样的话会导致并发清除的时候,用户线程可以了利用的空间不足。所以这里会浪费一些内存空间给用户线程预留。

                  3)应用场景

                    • CMS只会回收老年代和永久带(1.8开始为元数据区,需要设置CMSClassUnloadingEnabled),不会收集年轻带;
                    • CMS是一种预处理垃圾回收器,它不能等到old内存用尽时回收,需要在内存用尽前,完成回收操作,否则会导致并发回收失败;所以CMS垃圾回收器开始执行回收操作,有一个触发阈值,默认是老年代或永久带达到92%。

                    4)使用方式

                    -XX:+UseConcMarkSweepGC 激活CMS收集器

                    -XX:ConcGCThreads 设置CMS线程的数量

                    -XX:+UseCMSInitiatingOccupancyOnly 只根据老年代使用比例来决定是否进行CMS

                    -XX:CMSInitiatingOccupancyFraction 设置触发CMS老年代回收的内存使用率占比

                    -XX:+CMSParallelRemarkEnabled 并行运行最终标记阶段,加快最终标记的速度

                    -XX:+UseCMSCompactAtFullCollection 每次触发CMS Full GC的时候都整理一次碎片

                    -XX:CMSFullGCsBeforeCompaction=* 经过几次CMS Full GC的时候整理一次碎片

                    -XX:+CMSClassUnloadingEnabled 让CMS可以收集永久带,默认不会收集

                    -XX:+CMSScavengeBeforeRemark 最终标记之前强制进行一个Minor GC

                    -XX:+ExplicitGCInvokesConcurrent 当调用System.gc()的时候,执行并行gc,只有在CMS或者G1下该参数才有效

                    5.G1

                    G1,Garbage First,在JDK 1.7版本正式启用,是当时最前沿的垃圾收集器。G1可以说是CMS的终极改进版,解决了CMS内存碎片、更多的内存空间登问题。虽然流程与CMS比较相似,但底层的原理已是完全不同。

                    JDK1.9 默认垃圾收集器G1。

                    1)特点

                      • 高效益优先。G1会预测垃圾回收的停顿时间,原理是计算老年代对象的效益率,优先回收最大效益的对象。
                      • 堆内存结构的不同。以前的收集器分代是划分新生代、老年代、持久代等。G1则是把内存分为多个大小相同的区域Region,每个Region拥有各自的分代属性,但这些分代不需要连续。

                      下面是老式收集器和G1收集器的分代对比:

                      image.gif编辑

                      image.gif编辑

                        • 这样的分区可以有效避免内存碎片化问题。但会造成分代内存不连续,导致在GC搜索垃圾对象的时候需要全盘扫描找出引用内存所在。所以G1对于每个Region都维护一个Remembered Set,用于记录对象引用的情况。当GC发生的时候根据Remembered Set的引用情况去搜索。

                        2)整体流程

                          • 初始标记(initial mark),标记了从GC Root开始直接关联可达的对象。STW(Stop the World)执行。
                          • 并发标记(concurrent marking),并发标记初始标记的对象,此时用户线程依然可以执行。
                          • 最终标记(Remark),STW,标记再并发标记过程中产生的垃圾。
                          • 筛选回收(Live Data Counting And Evacuation),评估标记垃圾,根据GC模式回收垃圾。STW执行。

                          image.gif编辑

                          3)应用场景

                            • G1是一款面向服务端应用的垃圾收集器。
                            • 大堆(6左右,>=)运行,低GC延迟(<=0.5s)要求,
                            • GC频繁;收集或者压缩时间长;收集对象年龄差异大。

                            4)使用方式

                            -XX:+UseG1GC

                            不要设置年轻代大小 --Xmn;G1自动调整

                            XX:MaxGCPauseMillis:设置为能够满足90%请求的时间值。

                            6.ZGC

                            在JDK 11当中,加入了实验性质的ZGC。它的回收耗时平均不到2毫秒。它是一款低停顿高并发的收集器。ZGC几乎在所有地方并发执行的,除了初始标记的是STW的。所以停顿时间几乎就耗费在初始标记上,这部分的实际是非常少的。

                            image.gif编辑

                            1)特点

                              • 着色指针Colored Pointer。ZGC利用指针的64位中的几位表示Finalizable、Remapped、Marked1、Marked0(ZGC仅支持64位平台),以标记该指向内存的存储状态。相当于在对象的指针上标注了对象的信息。在这个被指向的内存发生变化的时候(内存在Compact被移动时),颜色就会发生变化。
                              • 读屏障Load Barrier 。由于着色指针的存在,在程序运行时访问对象的时候,可以轻易知道对象在内存的存储状态(通过指针访问对象),若请求读的内存在被着色了。那么则会触发读屏障。读屏障会更新指针再返回结果,此过程有一定的耗费,从而达到与用户线程并发的效果。

                              2)整体流程

                              与标记对象的传统算法相比,ZGC在指针上做标记,在访问指针时加入Load Barrier(读屏障),比如当对象正被GC移动,指针上的颜色就会不对,这个屏障就会先把指针更新为有效地址再返回,也就是,永远只有单个对象读取时有概率被减速,而不存在为了保持应用与GC一致而粗暴整体的Stop The World。

                              3)性能提升

                              ZGC的吞吐量与Parallel GC(优化吞吐量)大致相当,但平均暂停时间为1ms,最长为4ms。 与之相比G1和Parallel会有很多超过200ms的GC停顿。

                              三、补充

                              1.Stop-The-World

                              JVM在后台自动发起和自动完成的,在用户不可见的情况下,把用户正常的工作线程全部停掉,即GC停顿,这会带给用户不良的体验。从JDK1.3到现在,从Serial收集器-》Parallel收集器-》CMS-》G1,用户线程停顿时间不断缩短,但仍然无法完全消除。

                              2.为什么只有ParNew能与CMS收集器配合?

                              CMS是HotSpot在JDK1.5推出的第一款真正意义上的并发(Concurrent)收集器,第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。CMS作为老年代收集器,但却无法与JDK1.4已经存在的新生代收集器Parallel Scavenge配合工作,因为Parallel Scavenge(以及G1)都没有使用传统的GC收集器代码框架,而另外独立实现;而其余几种收集器则共用了部分的框架代码。所以如果老年代使用了CMS,那新生代只能使用ParNew。

                              3.CMS垃圾回收中,既然Mark Sweep会造成内存碎片,那么为什么不把算法换成Mark Compact呢?

                              答案其实很简答,因为当并发清除的时候,用Compact整理内存的话,原来的用户线程使用的内存还怎么用呢?要保证用户线程能继续执行,前提的它运行的资源不受影响嘛。Mark Compact更适合“Stop the World”这种场景下使用。

                              参考资料:

                                1. Serial收集器和ParNew收集器_wxy941011的博客-CSDN博客_serial parnew
                                2. 一文了解JVM全部垃圾回收器,从Serial到ZGC - 大天狗子 - 博客园
                                3. CMS垃圾收集器_mc90716_163的博客-CSDN博客
                                4. 五、Parallel Scavenge + Parallel Old 收集器 - 萧亦全 - 博客园
                                相关文章
                                |
                                24天前
                                |
                                缓存 算法 Java
                                JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
                                这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
                                50 4
                                JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
                                |
                                22天前
                                |
                                存储 监控 算法
                                美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
                                尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
                                美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
                                |
                                11天前
                                |
                                监控 算法 Java
                                深入理解Java的垃圾回收机制
                                【10月更文挑战第22天】在Java的世界里,有一个默默无闻却至关重要的角色——垃圾回收(Garbage Collection, GC)。就像城市的清洁工一样,它默默地清理着不再使用的内存空间,确保我们的程序运行得既高效又稳定。但你真的了解垃圾回收是如何工作的吗?让我们一起探索这个看似简单却充满奥秘的过程,看看它是如何影响你的Java应用性能的。
                                |
                                14天前
                                |
                                存储 算法 Java
                                Java虚拟机(JVM)的内存管理与性能优化
                                本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
                                |
                                21天前
                                |
                                存储 监控 算法
                                Java中的内存管理与垃圾回收机制解析
                                本文深入探讨了Java编程语言中的内存管理方式,特别是垃圾回收机制。我们将了解Java的自动内存管理是如何工作的,它如何帮助开发者避免常见的内存泄漏问题。通过分析不同垃圾回收算法(如标记-清除、复制和标记-整理)以及JVM如何选择合适的垃圾回收策略,本文旨在帮助Java开发者更好地理解和优化应用程序的性能。
                                |
                                26天前
                                |
                                算法 Java
                                谈谈HotSpot JVM 中的不同垃圾回收器
                                【10月更文挑战第5天】理解 HotSpot JVM 中的不同垃圾回收器(如 CMS、G1 和 ZGC)的区别,需要深入了解它们的设计原理、工作方式和应用场景。以下是对这三个垃圾回收器的简要概述以及一个示例 Java 程序,虽然示例程序本身不能直接展示垃圾回收器的内部机制,但可以帮助观察不同垃圾回收器的行为。
                                15 1
                                |
                                30天前
                                |
                                算法 Java 开发者
                                Java中的垃圾回收机制:从原理到实践
                                Java的垃圾回收机制(Garbage Collection, GC)是其语言设计中的一大亮点,它为开发者提供了自动内存管理的功能,大大减少了内存泄漏和指针错误等问题。本文将深入探讨Java GC的工作原理、不同垃圾收集器的种类及它们各自的优缺点,并结合实际案例展示如何调优Java应用的垃圾回收性能,旨在帮助读者更好地理解和有效利用Java的这一特性。
                                |
                                1月前
                                |
                                监控 算法 Java
                                Java中的内存管理:理解垃圾回收机制
                                【10月更文挑战第2天】 在本文中,我们将深入探讨Java编程语言中的内存管理机制,特别是垃圾回收机制。我们将从基本原理、垃圾回收算法到实际应用场景全面解析,帮助你更好地理解和优化Java应用的内存使用。无论你是初学者还是有经验的开发者,这篇文章都能带给你新的启发和思考。
                                31 2
                                |
                                1月前
                                |
                                算法 Java 程序员
                                深入理解Java的垃圾回收机制
                                【9月更文挑战第31天】在Java的世界里,有一个默默守护者,它负责清理不再使用的对象,确保内存的有效利用。这就是垃圾回收器(Garbage Collector, GC)。本文将带你一探究竟,了解它是如何工作的,以及为何我们需要关心它的存在。
                                |
                                23天前
                                |
                                存储 监控 算法
                                深入理解Java内存模型与垃圾回收机制
                                【10月更文挑战第10天】深入理解Java内存模型与垃圾回收机制
                                16 0