JVM垃圾回收,JVM垃圾回收机制详解

马肤
JVM垃圾回收是一种自动管理内存的机制,旨在自动识别和清理不再使用的对象,释放内存空间。垃圾回收器通过识别哪些对象正在被应用程序使用,哪些不再需要,从而避免内存泄漏和性能下降。垃圾回收是Java虚拟机(JVM)的核心功能之一,对于提高应用程序的效率和稳定性至关重要。

文章目录

    • 垃圾回收概述
    • Java 中为什么会有 GC 机制
    • 垃圾回收详解
    • 垃圾回收器的种类
    • STW(Stop-The-World)
    • 分代回收
    • 垃圾回收算法
    • 垃圾gc的全流程
    • 对于 Java 的 GC 哪些内存需要回收
    • JVM中的永久代中会发生垃圾回收
    • Java 8 和 Java 11 在垃圾回收
    • CMS,G1 垃圾回收器中的三色标记

      垃圾回收概述

      垃圾回收(Garbage Collection,GC)是指自动管理程序中不再使用的内存的一种机制。在 Java 中,垃圾回收是由 JVM 负责的,它会自动监视和回收不再使用的对象,以释放它们所占用的内存。

      垃圾回收的主要目的是解决两个问题:一是释放已分配但不再被引用的对象占用的内存,二是为新对象的分配提供足够的可用内存空间。

      Java 的垃圾回收算法通常包括以下几种:

      1. 标记-清除算法(Mark and Sweep):该算法会先标记所有存活的对象,然后清理掉所有未标记的对象。这种算法可能导致内存碎片化,影响内存分配效率。
      2. 复制算法(Copying):将堆分为两块,每次只使用其中一块。当当前使用的那一块内存填满时,将存活的对象复制到另一块内存中,然后进行一次全内存的清理操作。
      3. 标记-整理算法(Mark and Compact):在标记存活对象后,将存活对象向一端移动,然后清理掉边界外的内存。这种算法可以解决内存碎片化的问题。
      4. 分代算法(Generational Garbage Collection):根据对象存活的特征将堆分为不同的代,每一代使用不同的垃圾回收算法。新生代通常使用复制算法,老年代使用标记-整理算法。

        垃圾回收的性能和效率对应用程序的整体性能有着重要的影响。虽然垃圾回收可以减轻开发人员的工作负担,但不合理的垃圾回收机制可能会导致应用程序出现卡顿、停顿等性能问题。因此,了解垃圾回收的原理和算法,并通过合适的配置和优化,可以帮助应用程序获得更好的性能表现。

      Java 中为什么会有 GC 机制

      安全性考虑;

      减少内存泄露;

      减少程序员工作量

      垃圾回收是在内存中存在没有引用的对象或超过作用域的对象时进行。

      垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源。

      垃圾回收详解

      垃圾回收是在内存中存在没有引用的对象或超过作用域的对象时进行的。

      垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源

      GC垃圾收集(Garbage Collection),(内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃)Java提供的GC功能可以 自动监测对象是否超过作用域从而达到自动回收内存的目的,避免内存溢出造成的一系列问题。Java语言没有提供释放已分配内存的显示操作方法。

      对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是”可达的”,哪些对象是”不可达的”。当GC确定一些对象为”不可达”时,GC就有责任回收这些内存空间。

      新生代:存放生命周期较短的对象的区域。

      老年代:存放生命周期较长的对象的区域。

      综上:新生代基本采⽤复制算法,老年代采用标记整理算法。cms采用标记清理

      新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;

      老年代回收器一般采用的是标记-整理的算法进行垃圾回收。

      垃圾回收器的种类

      新生代回收器:Serial、ParNew、Parallel Scavenge

      老年代回收器:Serial Old、Parallel Old、CMS

      整堆回收器:G1

      JVM垃圾回收,JVM垃圾回收机制详解 第1张

      Serial收集器: 单线程的收集器,收集垃圾时,必须stop the world,使用复制算法。

      ParallelNew收集器: Serial收集器的多线程版本,也需要stop the world,复制算法。 新生代采用复制算法,老年代采用标记整理;

      Parallel Scavenge收集器: 针对新生代,采用复制收集算法新生代收集器,复制算法的收集器,并发的多线程收集器,目标是达 到一个可控的吞吐量。如果虚拟机总共运行100分钟,其中垃圾花掉1分钟,吞吐量就是99%。

      Serial Old收集器: 新生代采用复制,老年代采用标记清理,是Serial收集器的老年代版本,单线程收集器,使用标记整理算法。

      Parallel Old收集器(并行): 针对老年代,标记整理,是Parallel Scavenge收集器的老年代版本,使用多线程,标记-整理算法。

      CMS(Concurrent Mark Sweep) 收集器: 基于标记清理,是一种以获得最短回收停顿时间为目标的收集器,标 记清除算法,运作过程:初始标记,并发标记,重新标记,并发清除,收集结束会产生大量空间碎 片。

      G1收集器: 整体上是基于标记清理,局部采用复制;标记整理算法实现,运作流程主要包括以下:初始标记,并发标记,最终标记,筛选 标记。不会产生空间碎片,可以精确地控制停顿。

      CMS 收集器和 G1 收集器区别

      CMS 收集器老年代收集器,可以配合新生代 Serial 和 ParNew 收集器一起使用;

      G1 收集器收集范围老年代和新生代,不需要结合其他收集器使用;

      CMS 收集器以最小停顿时间为目标收集器;

      G1 收集器可预测垃圾回收停顿时间

      CMS 收集器使用“标记-清除”算法进行垃圾回收,容易产生内存碎片

      G1 收集器使用“标记-整理”算法,进行了空间整合,降低了内存空间碎片。

      g1和cms区别,吞吐量优先和响应优先的垃圾收集器选择

      CMS收集器:一款以获取最短回收停顿时间为目标的收集器,是基于“标记-清除”算法实现的,分为4个步骤:初始标记、并发标记、重新标记、并发清除。

      G1收集器:面向服务端应用的垃圾收集器,过程:初始标记;并发标记;最终标记;筛选回收。整体上看是“标记-整理”,局部看是“复制”,不会产生内存碎片。

      吞吐量优先的并行收集器:以到达一定的吞吐量为目标,适用于科学技术和后台处理等。

      响应时间优先的并发收集器:保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。

      ZGC和G1垃圾回收器

      ZGC和G1是Java虚拟机(JVM)中的两种不同的垃圾回收器(Garbage Collectors),它们各自有不同的设计目标和特点。

      ZGC(Z Garbage Collector)

      ZGC是由Oracle公司开发的一种低延迟垃圾回收器。它的主要设计目标是提供亚毫秒级的垃圾回收暂停时间,这对于需要高响应性和低延迟的应用场景(如云计算、实时交易系统等)至关重要。

      ZGC的特点包括:

      1. 并发性:ZGC使用并发标记和并发清理技术,可以在应用程序运行的同时进行垃圾回收,从而减少对应用程序的暂停时间。
      2. 基于区域的内存管理:ZGC将堆内存划分为多个区域(Regions),每个区域都有一个独立的内存管理线程。这种设计可以将垃圾回收的工作分散到多个线程上,提高回收效率。
      3. 压缩指针:ZGC使用压缩指针技术来减少内存占用。通过将对象的引用从64位压缩为32位,ZGC可以在不牺牲太多内存的情况下提高性能。

        G1(Garbage-First)

        G1是Java 7 Update 4及以后版本中引入的一种垃圾回收器。它的设计目标是提供一个既能满足低延迟需求,又能处理大内存堆的垃圾回收器。

        G1(Garbage First)垃圾收集器是当今垃圾回收技术最前沿的成果之一。早在JDK7就已加入JVM的收集器大家庭中,成为HotSpot重点发展的垃圾回收技术。同优秀的CMS垃圾回收器一样,G1也是关注最小时延的垃圾回收器,也同样适合大尺寸堆内存的垃圾收集,官方也推荐使用G1来代替选择CMS。G1最大的特点是引入分区的思路,弱化了分代的概念,合理利用垃圾收集各个周期的资源,解决了其他收集器甚至CMS的众多缺陷。

        G1的特点包括:

      4. 分代收集:G1在逻辑上将堆内存划分为年轻代(Young Generation)和老年代(Old Generation)。年轻代主要存储新创建的对象,老年代则存储存活时间较长的对象。这种设计可以根据对象的存活周期来优化垃圾回收。
      5. 基于区域的内存管理:G1将堆内存划分为多个大小相等的区域(Regions),每个区域都可以是Eden区、Survivor区或Old区。这种设计有助于减少内存碎片,提高内存利用率。
      6. 增量式标记:G1使用增量式标记算法,在应用程序运行的同时进行垃圾回收操作。这有助于减少单次垃圾回收的暂停时间,并将垃圾回收的工作均匀地分布在多个时间片段中。
      7. 全局的标记-整理算法:G1通过标记所有被引用的对象,然后将存活的对象向一端移动,并清除边界之外的对象。这种算法有助于减少内存碎片,提高内存利用率。

        总之,ZGC和G1都是针对现代应用程序需求而设计的垃圾回收器。ZGC注重低延迟和高并发性,而G1则更侧重于平衡低延迟和大内存堆的处理能力。在选择使用哪种垃圾回收器时,需要根据应用程序的具体需求进行评估和选择。

        G1 的全称是 Garbage-First,意为垃圾优先,哪一块的垃圾最多就优先清理它。

        G1 GC 最主要的设计目标是:将 STW 停顿的时间和分布,变成可预期且可配置的。

        被视为 JDK1.7 中 HotSpot 虚拟机的一个重要进化特征。它具备一下特点:

        并行与并发:G1 能充分利用 CPU、多核环境下的硬件优势,使用多个 CPU(CPU 或者 CPU 核心)来缩短 Stop-The-World 停顿时间。部分其他收集器原本需要停顿 Java 线程 执行的 GC 动作,G1 收集器仍然可以通过并发的方式让 java 程序继续执行。

        分代收集:虽然 G1 可以不需要其他收集器配合就能独立管理整个 GC 堆,但是还是保留 了分代的概念。

        空间整合:与 CMS 的“标记-清理”算法不同,G1 从整体来看是基于“标记-整理”算法 实现的收集器;从局部上来看是基于“标记-复制”算法实现的。

        可预测的停顿:这是 G1 相对于 CMS 的另一个大优势,降低停顿时间是 G1 和 CMS 共 同的关注点,但 G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明 确指定在一个长度为 M 毫秒的时间片段内。

        G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来)

      G1 会不会进行 Full GC

      会,当内存满了的时候就会进行 Full GC;且 JDK10 之前的 Full GC,为单线程的,所以使用 G1 需要避免 Full GC 的产生

      解决方案:

      加大内存;

      提高 CPU 性能,加快 GC 回收速度,而对象增加速度赶不上回收速度,则 Full GC 可以避免;

      降低进行 Mixed GC 触发的阈值,让 Mixed GC 提早发生(默认 45%)

      STW(Stop-The-World)

      在编程和系统设计的上下文中,“STW” (Stop-The-World) 通常指的是一个操作或事件,它会导致整个系统或应用程序的某一部分暂时停止执行,以便进行某些操作,如垃圾回收、内存整理或线程同步。

      在Java虚拟机(JVM)中,“Stop-The-World” 垃圾回收(STW GC)是一个常见的概念。当JVM进行垃圾回收时,它会暂停所有的应用程序线程,以便扫描和回收不再使用的对象。这个过程会导致应用程序的短暂停顿,因此被称为"Stop-The-World"。

      虽然STW GC可以确保垃圾回收的准确性和一致性,但它对应用程序的性能和响应性有负面影响。因此,现代JVM和垃圾回收算法都在努力减少STW的持续时间,或者通过并发和增量收集等技术来避免完全的STW。

      在其他系统或应用中,“Stop-The-World” 的概念可能略有不同,但通常都指的是一个需要暂停整个系统或应用程序以便进行某些操作的过程。

      为了减少STW对应用程序的影响,可以采取以下策略:

      1. 选择合适的垃圾回收算法:某些垃圾回收算法(如G1或ZGC)被设计为减少STW的持续时间或避免完全的STW。
      2. 优化代码和内存使用:通过减少内存分配和不必要的对象创建,可以降低垃圾回收的频率和持续时间。
      3. 监控和调整JVM参数:通过监控JVM的性能指标并调整相关参数,可以优化垃圾回收的性能。
      4. 使用并发和增量收集技术:这些技术可以在不完全暂停应用程序的情况下进行垃圾回收。
      5. 分布式系统设计:在分布式系统中,通过将工作负载分散到多个节点上,可以减少单个节点上STW的影响。

      分代回收

      因为Java对象基本上都是临时的对象,很快就会被回收。所以,JVM的内存是分代的设计,根据对象在内存中的存活时间,分为年轻代、老年代和永久代

      年轻代,采用的是标记复制算法,在每次复制的时候,存活下来的对象会很少。

      老年代,是经历过几次GC的对象,JVM会认为它可能继续存活下去,就不太适合采用标记复制算法,所以,老年代采用的是标记清除算法,比如CMS这种回收器就是采用标记清除的方式。

      永久代,表示一直会存活的对象,只有在触发Full GC的时候才会被回收。所以,永久代对象创建过多的话,比较容易出现内存溢出。最典型的场景就是,在JSP页面比较多的情况,容易出现永久代内存溢出。

      为什么要这样分代

      其实主要原因就是可以根据各个年代的特点进行对象分区存储,更便于回收,采用最适当的收集算 法:

      新生代中,每次垃圾收集时都发现大批对象死去,只有少量对象存活,便采用了复制算法, 只需要付出少量存活对象的复制成本就可以完成收集。 而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须采用“标记-清理”或 者“标记-整理”算法。

      新生代又分为Eden和Survivor (From与To,这里简称一个区)两个区。加上老年代就这三个区。 数据会首先分配到Eden区当中(当然也有特殊情况,如果是大对象那么会直接放入到老年代(大 对象是指需要大量连续内存空间的java对象)。当Eden没有足够空间的时候就会触发jvm发起一次 Minor GC,。如果对象经过一次Minor-GC还存活,并且又能被Survivor空间接受,那么将被移动 到Survivor空间当中。并将其年龄设为1,对象在Survivor每熬过一次Minor GC,年龄就加1,当 年龄达到一定的程度(默认为15)时,就会被晋升到老年代中了,当然晋升老年代的年龄是可以 设置的。

      垃圾回收算法

      年轻代 标记复制算法

      老生代 标记整理算法

      永久代 Full GC

      标记清除算法、标记复制算法和标记整理算法

      标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。

      复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块

      上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。

      标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。

      分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。

      三种GC算法的优缺点

      标记清除算法。它主要的缺点是会产生比较多的内存碎片,而这些内存碎片,随着系统运行时间的推移,系统运行时间长了以后,无法再大量分配连续的内存空间。这样的话,就会导致更加频繁地触发GC操作

      标记复制算法,它主要的缺点是,会导致我们实际使用的内存空间只有50%,而另外50%的内存空间是闲置的。所以,比较浪费内存空间。而且,如果大量复制对象的话,垃圾回收的耗时就比较长了,所以,这种算法,更加适合处理存活对象少,垃圾对象比较多的场景

      标记整理算法。它的本质和标记清除算法是基本差不多,只不过标记整理算法多了一个动作,会把存活的对象移动到一起。从垃圾收集和清理效率来看,增加了一个移动的动作,所以耗时会更久。但是,新分配对象的耗时就会下降

      标记清除算法

      它是将存活的对象打上标记,那么没有被标记的对象就是需要被回收的垃圾对象,这些垃圾对象会被垃圾回收器直接回收。

      标记复制算法

      它是把内存分为两等份,每次只使用其中的一份,等到正在使用的这部分内存满了之后,就会标记出存活的对象,然后把存活的对象拷贝到另一部分闲置的内存中,那留在另一部分内存中的对象,会全部被垃圾回收器回收。那么,原来空闲的内存空间就会变成使用中的状态,而原来使用中的内存空间会被闲置出来继续使用。这就是标记复制算法的一次完整的GC。然后,一直重复这个循环

      标记整理算法

      它是先标记出存活的对象,然后,把所有存活的对象整理到内存空间的另一端,而没有被标记的对象就是可以被覆盖或者是释放。

      垃圾gc的全流程

      JVM垃圾回收,JVM垃圾回收机制详解 第2张

      GC流程是每一个Java开发人员都应该掌握的内容。你知道什么时候触发Minor GC?什么时候触发

      Minor GC 的过程是怎么样的?Full GC 的过程又是怎么样的?

      挤满新生代的最后一个对象

      我们应当知道,新创建的对象一般会被分配在新生代中。常用的新生代的垃圾回收器是 ParNew 垃圾回收器,它按照 8:1:1 将新生代分成 Eden 区,以及两个 Survivor 区。

      某一时刻,我们创建的对象将 Eden 区全部挤满,这个对象就是「挤满新生代的最后一个对象」。此时,Minor GC 就触发了。

      正式 Minor GC 前的检查

      在正式 Minor GC 前,JVM 会先检查新生代中对象,是比老年代中剩余空间大还是小。为什么要做这样的检查呢?原因很简单,假如 Minor GC 之后 Survivor 区放不下剩余对象,这些对象就要进入到老年代,所以要提前检查老年代是不是够用。这样就有两种情况:

      1. 老年代剩余空间大于新生代中的对象大小,那就直接 Minor GC,GC 完 survivor 不够放,老年代也绝对够放
      2. 老年代剩余空间小于新生代中的对象大小,这个时候就要查看是否启用了「老年代空间分配担保规则」,具体来说就是看-XX:-HandlePromotionFailure参数是否设置了(一般都会设置)

        老年代空间分配担保规则是这样的。如果老年代中剩余空间大小,大于历次 Minor GC 之后剩余对象的大小,那就允许进行 Minor GC。因为从概率上来说,以前的放的下,这次的也应该放的下。那就有两种情况:

      3. 老年代中剩余空间大小,大于历次 Minor GC 之后剩余对象的大小,进行 Minor GC
      4. 老年代中剩余空间大小,小于历次 Minor GC 之后剩余对象的大小,进行 Full GC,把老年代空出来再检查

        Minor GC 后的处境

        前面说了,开启老年代空间分配担保规则只能说是大概率上来说,Minor GC 剩余后的对象够放到老年代,所以当然也会有万一,Minor GC 后会有这样三种情况:

      5. Minor GC 之后的对象足够放到 Survivor 区,皆大欢喜,GC 结束
      6. Minor GC 之后的对象不够放到 Survivor 区,接着进入到老年代,老年代能放下,那也可以,GC 结束
      7. Minor GC 之后的对象不够放到 Survivor 区,老年代也放不下,那就只能 Full GC

        实在不行只能 OOM

        前面都是成功 GC 的例子,还有 3 中情况,会导致 GC 失败,报 OOM:

      8. 紧接上一节 Full GC 之后,老年代任然放不下剩余对象,就只能 OOM
      9. 未开启老年代分配担保机制,且一次 Full GC 后,老年代任然放不下剩余对象,也只能 OOM
      10. 开启老年代分配担保机制,但是担保不通过,一次 Full GC 后,老年代任然放不下剩余对象,也是能 OOM

        GC作用区域:方法区和堆

        JVM在进行GC时,并不是对三个区统一回收

        大部分都是回收新生代

        轻GC(普通GC),重GC(全局GC)

        新生代 :Eden、Survive to、Survive from

        老年代

        MinorGC,MajorGC, FullGC

        MinorGC 清理整合新生代: eden 的清理,幸存者区的清理

        Minor GC触发条件:当Eden区满时,触发Minor GC。

        MajorGC清理整个OldGen的内存空间

        FullGC 清理整个堆空间—包括年轻代和永久代。

        Full GC触发条件:

        (1)调用System.gc时,系统建议执行Full GC,但是不必然执行

        (2)老年代空间不足

        (3)方法区空间不足

        (4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存

        (5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

      jvm中一次完整的GC流程(从ygc到fgc)是怎样的,重点讲讲对象如何晋升到老年代等

      答:对象优先在新生代区中分配,若没有足够空间,Minor GC;

      大对象(需要大量连续内存空间)直接进入老年态;长期存活的对象进入老年态。如果对象在新生代出生并经过第一次MGC后仍然存活,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。

      GC如何判断对象可以被回收

      引用计数法:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计 数为0时可以回收,(其他编程语言采用)

      可达性分析法:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。(java采用)

      System.gc()和Runtime.gc()会做什么事情,这两个方法用来提示JVM要进行垃圾回收。但是,立即开始还是延迟进行垃圾回收是取决于JVM的。

      垃圾回收器可以马上回收内存,程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。

      对象不可达,一定会被垃圾收集器回收么

      即使不可达,对象也不一定会被垃圾收集器回收,1)先判断对象是否有必要执行 finalize()方法,对象必须重写 finalize()方法且没有被运行过。2)若有必要执行,会把对象放到一个队列中,JVM 会开一个线程去回收它们,这是对象最后一次可以逃逸清理的机会。

      哪些情况下的对象会被垃圾回收机制处理掉

      利用可达性分析算法,虚拟机会将一些对象定义为 GC Roots,从 GC Roots 出发沿着引用链向下寻找,如果某个对象不能通过 GC Roots 寻找到,虚拟机就认为该对象可以被回收掉。

      哪些对象可以被看做是 GC Roots ?

      1)虚拟机栈(栈帧中的本地变量表)中引用的对象;

      2)方法区中的类静态属性引用的对象,常量引用的对象;

      3)本地方法栈中 JNI(Native 方法)引用的对象;

      什么时候触发full gc

      清理整个堆空间—包括年轻代和老年代和永久代

      因为Full GC是清理整个堆空间所以Full GC执行速度非常慢,在Java开发中最好保证少触发Full GC

      (1)调⽤System.gc时,系统建议执行Full GC,但是不必然执行

      (2)老年代空间不足

      (3)方法去空间不足

      (4)通过Minor GC后进入老年代的平均大小大于老年代的可⽤内存

      (5) 由Eden区 、From Space区 向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

      强引用、软引用、弱引用、虚引用以及他们之间和gc的关系

      强引用:new出的对象之类的引用,

      只要强引用还在,永远不会回收

      软引用:引用但非必须的对象,内存溢出异常之前,回收

      弱引用:非必须的对象,对象能生存到下一次垃圾收集发生之前。

      虚引用:对生存时间无影响,在垃圾回收时得到通知。

      对于 Java 的 GC 哪些内存需要回收

      内存运行时 JVM 会有一个运行时数据区来管理内存。它主要包括 5 大部分:程序计数器(Program Counter

      Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap).

      而其中程序计数器、虚拟机栈、本地方法栈是每个线程私有的内存空间,随线程而生,随线程而亡。例如栈中每一个栈帧中分配多少内存基本上在类结构确定是哪个时就已知了,因此这 3 个区域的内存分配和回收都是确定的,无需考虑内存回收的问题。但方法区和堆就不同了,一个接口的多个实现类需要的内存可能不一样,我们只有在程序运行期间才会知道会创建哪些对象,这部分内存的分配和回收都是动态的,GC 主要关注的是这部分内存。

      总而言之,GC 主要进行回收的内存是 JVM 中的方法区和堆

      JVM中的永久代中会发生垃圾回收

      垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full

      GC)。如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确

      的永久代大小对避免Full GC是非常重要的原因。请参考下Java8:从永久代到元数据区

      (注:Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区)

      永久代就是JVM的方法区。在这里都是放着一些被虚拟机加载的类信息,静态变量,常量等数据。

      这个区中的东西比老年代和新生代更不容易回收。

      Java 8 和 Java 11 在垃圾回收

      java8和ava11都是lts版本的dk,所以会有人经常问他们之间的区别。特别是在gc上面的差别。

      首先,在垃圾收集器上面,java8中默认的parallelscavengegc+paralleloldgc的,分别用来做新

      生代和老年代的垃圾回收。而在java11中默认采用的是g1进行整堆回收的(ava9中就是默认的了).

      另外,java11中还新增了一种垃圾收集器,那就是zgc,他可以在保证高吞吐量的同时保证最短的暂停时但

      间。

      Java 8 和 Java 11 在垃圾回收(Garbage Collection,GC)方面有一些区别。以下是它们之间的主要区别:

      1. G1 GC 的默认启用:在 Java 8 中,Parallel GC 是默认的垃圾回收器,而在 Java 11 中,G1 GC 成为了默认的垃圾回收器。G1 GC(Garbage-First Garbage Collector)是一种面向服务时间的垃圾回收器,旨在提供更稳定和可预测的暂停时间。
      2. 垃圾回收策略改进:Java 11 中对 G1 GC 进行了一些改进,包括更好的内存分配、并行阶段优化、根扫描阶段优化等。这些改进使得 G1 GC 在大多数情况下能够提供更好的性能和吞吐量。
      3. Epsilon GC 的引入:Java 11 引入了一种新的实验性垃圾回收器,称为 Epsilon GC。Epsilon GC 是一种无操作的垃圾回收器,它可以用于短暂测试或特定场景下,以避免由于垃圾回收带来的延迟。
      4. ZGC 的引入:Java 11 引入了另一种实验性垃圾回收器,称为 ZGC。ZGC 是一种并发的、低延迟的垃圾回收器,旨在处理大内存和低延迟的应用场景。它允许非常短暂的停顿时间,并且能够处理 TB 级别的堆内存。

        需要注意的是,这些区别并不是 Java 8 和 Java 11 的垃圾回收器特性的全部内容。Java 平台一直在不断改进和优化垃圾回收器,因此还可能存在其他细微变化和优化。建议在具体使用时参考官方文档和相关资料,以获取最准确和最新的信息。

      CMS,G1 垃圾回收器中的三色标记

      三色标记法是一种垃圾回收法,它可以让 JVM 不发生或仅短时间发生 STW(Stop The World),从而达到清除 JVM 内存垃圾的目的。

      三色标记法将对象的颜色分为了黑、灰、白,三种颜色。

      黑色:该对象已经被标记过了,且该对象下的属性也全部都被标记过了。(程序所需要的对象);

      灰色:对象已经被垃圾收集器扫描过了,但是对象中还存在没有扫描的引用(GC 需要从此对象 中去寻找垃圾);

      白色:表示对象没有被垃圾收集器访问过,即表示不可达。

      CMS 解决办法:增量更新

      在应对漏标问题时,CMS 使用了增量更新(Increment Update)方法来做,在一个未被标记的 对象(白色对象)被重新引用后,引用它的对象若为黑色则要变成灰色,在下次二次标记时让 GC 线程继续标记它的属性对象(但还是存在漏标的问题)。

      CMS 另两个致命缺陷

      CMS 采用了 Mark-Sweep 算法,最后会产生许多内存碎片,当到一定数量时,CMS 无法清 理这些碎片了,CMS 会让 Serial Old 垃圾处理器来清理这些垃圾碎片,而 Serial Old 垃圾处 理器是单线程操作进行清理垃圾的,效率很低。

      所以使用 CMS 就会出现一种情况,硬件升级了,却越来越卡顿,其原因就是因为进行 Serial Old GC 时,效率过低。

      解决方案:使用 Mark-Sweep-Compact 算法,减少垃圾碎片

      调优参数(配套使用):

      -XX:+UseCMSCompactAtFullCollection 开启 CMS 的压缩

      -XX:CMSFullGCsBeforeCompaction 默认为 0,指经过多少次 CMS FullGC 才进行压缩

      当 JVM 认为内存不够,再使用 CMS 进行并发清理内存可能会发生 OOM 的问题,而不得不 进行 Serial Old GC,Serial Old 是单线程垃圾回收,效率低

      解决方案:降低触发 CMS GC 的阈值,让浮动垃圾不那么容易占满老年代

      调优参数:

      -XX:CMSInitiatingOccupancyFraction 92% 可以降低这个值,让老年代占用率达到该值就 进行 CMS GC

      G1 解决办法:SATB

      SATB(Snapshot At The Beginning), 在应对漏标问题时,G1 使用了 SATB 方法来做,具体流

      程: 在开始标记的时候生成一个快照图标记存活对象

      在一个引用断开后,要将此引用推到 GC 的堆栈里,保证白色对象(垃圾)还能被 GC 线

      程扫描到(在**write barrier(写屏障)**里把所有旧的引用所指向的对象都变成非白的)

      配合 Rset,去扫描哪些 Region 引用到当前的白色对象,若没有引用到当前对象,则回收


0
收藏0
文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

相关阅读

  • 【研发日记】Matlab/Simulink自动生成代码(二)——五种选择结构实现方法,Matlab/Simulink自动生成代码的五种选择结构实现方法(二),Matlab/Simulink自动生成代码的五种选择结构实现方法详解(二)
  • 超级好用的C++实用库之跨平台实用方法,跨平台实用方法的C++实用库超好用指南,C++跨平台实用库使用指南,超好用实用方法集合,C++跨平台实用库超好用指南,方法与技巧集合
  • 【动态规划】斐波那契数列模型(C++),斐波那契数列模型(C++实现与动态规划解析),斐波那契数列模型解析与C++实现(动态规划)
  • 【C++】,string类底层的模拟实现,C++中string类的模拟底层实现探究
  • uniapp 小程序实现微信授权登录(前端和后端),Uniapp小程序实现微信授权登录全流程(前端后端全攻略),Uniapp小程序微信授权登录全流程攻略,前端后端全指南
  • Vue脚手架的安装(保姆级教程),Vue脚手架保姆级安装教程,Vue脚手架保姆级安装指南,Vue脚手架保姆级安装指南,从零开始教你如何安装Vue脚手架
  • 如何在树莓派 Raspberry Pi中本地部署一个web站点并实现无公网IP远程访问,树莓派上本地部署Web站点及无公网IP远程访问指南,树莓派部署Web站点及无公网IP远程访问指南,本地部署与远程访问实践,树莓派部署Web站点及无公网IP远程访问实践指南,树莓派部署Web站点及无公网IP远程访问实践指南,本地部署与远程访问详解,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南。
  • vue2技术栈实现AI问答机器人功能(流式与非流式两种接口方法),Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法探究,Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法详解
  • 发表评论

    快捷回复:表情:
    评论列表 (暂无评论,0人围观)

    还没有评论,来说两句吧...

    目录[+]

    取消
    微信二维码
    微信二维码
    支付宝二维码