JVM之GC(垃圾收集器)(第四篇)

本文详细介绍了JVM中的垃圾回收器,包括GC的基本概念、分类及常见收集器如Serial、ParNew、Parallel、CMS和G1的特点和工作原理。GC主要分为串行和并行,强调了并发、停顿时间和吞吐量等因素对性能的影响。CMS和G1作为并发收集器,力求缩短暂停时间,但CMS存在浮动垃圾和碎片问题,而G1通过Region概念提高了效率和预测性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3.1 概念

GC 垃圾回收器是 JVM 中自动内存管理机制的具体实现,在 HotSpot 虚拟机中 GC 的工作主要划分为两
大类,分别是内存动态分配和垃圾回收,在内存执行分配之前,GC 首先会对内存空间进行划分,考虑到 JVM
中存活对象的生命周期会具有两极化,应该采取不同的垃圾收集策略,分代收集可以实现这个目标,目前几
乎所有的GC 都是采用分代收集算法执行垃圾回收
一般来说当内存空间中的内存消耗到达一定阈值之后,GC 就会执行垃圾回收,而且回收算法必须非常准确,
一定不能造成内存中存活的对象被错误的回收掉,也不能造成已经死亡的对象没有及时回收,而且 GC 执行
内存回收的时候应该做到高效,不应该导致程序长时间的暂停,以及要避免产生内存碎片,不过 GC 回收垃
圾的时候不可避免的会产生碎片,因为被回收的对象空间不是连续的,这样一来会导致没有足够的空间分
配给大内存对象,不过可以通过压缩算法来消除碎片
可以通过以下六点来评估一个 GC 的性能

  1. 吞吐量:程序的运行时间(程序时间+回收时间)
  2. 垃圾回收开销: 吞吐量的补数,垃圾回收器所占时间与总时间的比例
  3. 暂停时间: 执行垃圾回收的时候,程序的工作线程被暂停的时间
  4. 收集频率: 相对于程序的执行,收集操作发生的频率
  5. 堆空间: Java 堆占用的空间大小
  6. 快速: 一个对象从创建到被回收所经历的时间

3.2 垃圾回收器分类

由于 JDK 的高速迭代,Java 到现在已经衍生了很多版本的 GC,比如 Serial/Serial Old 收集器,ParNew 收集器,Parallel/Parallel Old 收集器,CMS(Concurrent-Mark-Sweep) 收集器,以及从 JDK7U4版本开始的出现的G1(Garbage-First)收集器等
按照不同的划分角度,可以将 GC 分为 不同的类型
按照 线程数 划分 可以分为 串行垃圾回收器并行垃圾回收器

  1. 串行回收指的是同一段时间内只允许一件事情发生,当有多个 CPU 的时候也只能有一个 CPU 用于
    执行垃圾回收操作,并且在执行回收的时候,程序中的工作线程会被暂停,回收结束后才会恢复,这就是
    串行回收,一般情况下串行回收被用在 client 模式下,和串行回收相反,并行回收可以使用多个 CPU 来
    执行垃圾回收,因此提升了应用的吞吐量,不过并行回收仍然使用 STW和复制算法
    串行回收有两个特点:首先 它仅仅使用单个线程进行垃圾回收, 其次它是 独占式 的垃圾回收方式
  2. 年轻代串行回收器使用复制算法,实现相对简单,逻辑处理特别高效,而且没有额外的线程切换开销,
    在诸如单 CPU 或者较小的应用内存等硬件平台,它的性能可以超过并行回收器
    并行收集器是工作在新生代 垃圾回收器,它只是简单的将串行回收器多线程话,它的回收策略算法
    以及参数和串行一致,并行回收器也是独占式的回收器,在收集过程中,也会 STW,不过在并发能力强
    的 CPU 上面,它产生的停顿时间小于串行收集器,效率更高
    按照 工作模式 分可以划分为 并发式回收器 和 独占式回收器 ,并发式(注意不是并行)回收器与应用程序
    线程交替执行,以尽量减少应用程序的停顿时间,独占式垃圾回收器一旦运行就停止应用中的其他线程,直
    到回收结束
    按照 碎片处理 方式分可以分为 压缩式垃圾回收器 和 非压缩式垃圾回器 ,压缩式垃圾回收器会在回收完
    成后堆存货对象进行压缩整理,消除回收后的碎片,非压缩式的垃圾回收器不会进行此过程
    按照 工作内存区间 划分又可以分为 年轻代垃圾回收器 和 年老代垃圾回收器

3.3常见垃圾回收器

3.3.1 Serial 收集器
Serial收集器是JAVA虚拟机中最基本、历史最悠久的收集器,在JDK 1.3.1之前是JAVA虚拟机 新生代 收集的唯一选择。Serial收集器是一个串行垃圾收集器,但它的“单线程”的意义并不仅仅是说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束,通过 -XX:+UseSerialGC 指定使用 Sarial 收集器

Serial收集器到JDK1.7为止,它依然是JAVA虚拟机运行在Client模式下的 默认新生代收集器 。它也有着优于其他收集器的地方:简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。在用户的桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大,收集几十兆甚至一两百兆的新生代(仅仅是新生代使用的内存,桌面应用基本上不会再大了),停顿时间完全可以控制在几十毫秒最多一百多毫秒以内,只要不是频繁发生,这点停顿是可以接受的。所以,Serial收集器对于运行在低硬件资源的应用来说是一个很好的选择

Seiral Old 收集器
除了年轻代之外,Seiral 还提供了用于执行年老代垃圾收集的 Seiral Old 收集器, Seiral Old 收集器同样也采
用了串行回收和 STW 机制,只不过回收算法为标记-压缩算法,在 JVM 受制于单个 CPU 核心的环境下,使用
Seiral 收集器和 Seiral Old 收集器组合执行 Client 模式下单内存回收将会是不错的选择,基于串行回收的
收集器适用于大多数对于暂停时间要求不高的 Client 模式下的 JVM,由于 Seiral 收集器是一个串行的独占
式的收集器,所以在堆空间比较大的应用程序中,一旦老年代的串行收集器启动,应用程序很可能会卡顿几
秒甚至更长时间
Serial/Serial Old组合收集器运行示意图如下:
3.3.2 ParNew 收集器
ParNew收集器是JAVA虚拟机中垃圾收集器的一种。它是Serial收集器的多线程版本,除了使用多条线程
进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio、-
XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、对象分配规
则、回收策略等都与Serial收集器一致,通过使用 -XX:+UseParNewGC 来指定使用 ParNew 收集器
需要注意的是,ParNew在单核甚至双核环境下绝对不会有比Serial收集器更好的效果,但是随着CPU数
量的增加ParNew相较于Serial的优势会越来越明显,但并不是成倍增长的,原因还是那个,多线程切换
的开销
在Server模式下,ParNew收集器是一个非常重要的收集器,因为除了Serial收集器外,目前只有它能与
CMS收集器配合工作;但在单个CPU环境中,不会比Serail收集器有更好的效果,因为存在线程交互开销
QF-JAVA
为什么只有ParNew能与CMS收集器配合:
CMS是HotSpot在JDK1.5推出的第一款真正意义上的并发(Concurrent)收集器,第一次实现了让垃圾收
集线程与用户线程(基本上)同时工作;
CMS作为老年代收集器,但却无法与JDK1.4已经存在的新生代收集器Parallel Scavenge配合工作;这是因
为Parallel Scavenge(以及G1)都没有使用传统的GC收集器代码框架,而另外独立实现,而其余几种收
集器则共用了部分的框架代码。
3.3.3 Parallel 收集器
首先第一点,也是针对Eden内存回收,它是从java 5开始出现,也是多线程(并行)GC收集器,采用的也是
跟ParNew一样的,标记复制算法;唯一的区别在于Parallar scavenge更注重吞吐量,通过相关JVM配置
可以让JVM调整Heap相关区域的内存大小,从而达到能调整吞吐量的效果;因此它也被称为吞吐量优先
的垃圾收集器,另外,它还有个悲观策略,因为该策略,有时候看到Old还有剩余空间却发生了Full GC,可
以通过 -XX:+UseParallelGC 来开启新生代Parallel收集器
需要注意点事,垃圾收集器中的吞吐量和低延迟这两个目标是相互矛盾的,如果要选择吞吐量,一定需要降
低垃圾收集的频率,但是这样又会导致在 GC 的时候需要更长时间来回收,反之为了保证低延迟,降低回收
的暂停时间,就必须频繁回收对象,这样又引起了年轻代内存的缩减和程序的吞吐量下降
同样,Parallel 收集器也提供了用于执行老年代垃圾收集的Parallel Old 收集器,采用的是标记-压缩算法,也
是基于并行收集和 STW,在吞吐量优先的场景,使用Parallel 收集器和Parallel Old收集器 组合,在 Server 模
式下性能不错,通过-XX:+UseParallel OldGC 开启年老代和年轻代都使用Parallel 收集器
3.3.4 CMS 收集器

  1. CMS(Concurrent Mark Sweep)收集器是一种获取最短回收停顿时间为目标的收集器,工作在老年代
  2. CMS收集器是一种基于"标记-清除"算法实现的收集器,整个过程分为四步
    初始标记(CMS initial mark).该过程分为两步:
    标记GC Roots可达的老年代对象
    遍历新生代对象,标记可达的老年代对象
    并发标记(CMS concurrent mark)
    重新标记 (CMS remark)
    并发清除 (CMS concurrent sweep)
  3. CMS 执行生命周期以一个称为 初始标记阶段 开始,在这个阶段程序所有的工作线程都会因为 STW
    而暂停,这个阶段的主要任务就是标记内存中那些被根对象集合所链接的对象是否可达,一旦标记完
    成就会恢复被暂停的工作线程
  4. 并发标记阶段, 这个阶段仍然会触发 STW,这个阶段的主要任务是将之前不可达的对象标记为垃圾
    对象,在执行最后回收之前,尽管这些看起来是垃圾对象,但是由于在并发标记阶段,程序中的工作线程
    QF-JAVA
    和垃圾收集线程交替执行,因此在并发阶段无法保证被标记为垃圾的对象引用关系遭到破坏,为了解
    决这个问题 ,CMS 会再次进入标记阶段,这样程序会因为 STW 再次在暂停
  5. 重新标记阶段 ,对之前的对象进行再次标记,用于检查状态是否被改变
  6. 并发清除 清除无用(被标记的)对象。此阶段的回收是并发性的,非独占式的
    CMS优点
    (1)并发收集
    (2)尽可能降低停顿
    真的是一个很优秀的垃圾收集器!
    CMS 收集器的缺点
  7. CMS收集器对CPU非常敏感 。其实面向并发设计的程序都对CPU比较敏感,在并发阶段,虽然不会
    导致用户线程停顿,但是会因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐
    量降低。CMS默认启动的回收线程数是(CPU数量+3)/4,也就是当CPU在4个以上时,并发回收时,垃
    圾收集线程不少于25%的CPU资源,并且随着CPU数量的增加而下降。但是当CPU数量不足4个时,
    CMS对用户程序的影响就可能变的很大。如果CPU负载本来就很大,还要分出一半的运行能力去执
    行收集器线程,就可能导致用户程序的执行能力下降50%
  8. CMS收集器无法处理浮动垃圾 。可能出现"Concurrent Mode Failure"失败而导致另一次Full GC。由
    于CMS并发清理阶段用户线程还在运行着,伴随着程序的运行自然就还会有新的垃圾不断产生,这
    一部分产生的垃圾,CMS无法在当次收集中处理掉他们,只要留待下一次GC再进行处理。这一部
    分产生的垃圾成为"浮动垃圾"。也是由于在垃圾收集阶段用户线程还要继续运行,那也就还需要预
    留足够的内存空间给用户线程使用。因此CMS收集器不能像其他收集器那样,等到老年代被填满后
    再进行垃圾回收。需要预留一部分空间供并发收集时的程序运行使用。
  9. 由于CMS采用"标记-清理"算法实现,所以当垃圾收集后, 会产生大量的内存碎片 。空间碎片过
    多,当程序运行需要分配大对象时,由于找不到连续的内存空间,而不得不提前触发一次Full
    GC.CMS采用了-XX:+UseCMSCompactAtFullCollection开关参数,用于在CMS收集器顶不住要进行Full
    GC时开启内存碎片的合并整理过程。内存碎片的整理是无法并发执行的,空间碎片问题没有了,
    但是随之而来的停顿时间变长了。因此,虚拟机设计者还提供了一个参数:-
    QF-JAVA
    XX:CMSFullGCsBeforeCompaction来进行设置执行多少次不压缩的Full GC后进行一次带压缩的(默认
    是0.标识每次进入Full GC后都会带有一次内存压缩)
    什么时候使用CMS收集器
    如果应用程序对停顿比较敏感,并在在应用程序运行时可以提供更大的内存和更多的CPU(硬件相当牛
    逼),那么使用CMS收集器会给我们带来很多好处。还有就是如果JVM中,有相对较多存货时间较长的对
    象(老年代比较大)会更适合使用CMS
    3.3.5 G1回收器
    概念
    G1 Garbage First, JDK7U4版本时候推出的新的垃圾收集器,G1采取了与之前的三个完全不同方式,从
    而解决了前面三个的很多缺陷,从长期目标来说,G1是为了取代 CMS 存在的,G1引入了 Region 区域概
    念,从而避免了 GC 操作需要在整个 Java 堆或者整个年代进行,从而节省大量时间,提高吞吐量,降低
    延迟
    G1是一个压缩式的并行独占分代收集器,和其他的收集器一样,当一个年轻代收集执行时,整个年轻代
    会被回收,所有的应用线程都会被暂停,G1会启动多线程进行年轻代回收,和年轻代不一样.老年代的
    G1收集器和其他的收集器不一样,G1的老年代不需要整个老年代被回收,一次只需要扫描/回收一小
    部分的老年代 Region 就可以了,此外,需要注意的是这个老年代 Region 是和年轻代一起回收的,和
    CMS 类型,当老年代空间耗尽的时候,G1 GC 回启动一个失败保护的应急机制(FULL GC),改机制会收集
    压缩整个老年代
    特点
    从分代上来说,G1仍然是区分年轻代和老年代,并且年轻代还是分为 Eden 去和 Survivor 区,但是从堆
    结构上来说,它并不要求整个 Eden 区,年轻代或者老年代包含的 Region 是物理连接的,G1使用了全新
    的分区算法.特点如下:
  10. 并行性: 在回收期间,可以有多个 GC 线程同时工作,可以有效的利用多核的计算能力
  11. 并发行: G1拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此不会在整
    个回收阶段发生完全阻塞应用的的情况
  12. 分代 GC: G1依然是一个分代 收集器,和其他的收集器不同,它同时兼顾了年轻代和老年代,而其
    他收集器,或者工作在年轻代或者工作在老年代
  13. 空间整理: G1在回收过程中会对对象进行适当的移动,不像 CMS 那样只是简单的标记清理对
    象,在若干次回收后,CMS 必须进行一次内存清理,而 G1不同,他每次回收都会有效的复制对象,减
    少空间碎片,进而提高内部循环速度
  14. 可预见性: 由于分区的原因,G1可以只选取部分区域进行内存h回收,这样缩小了回收的范围,因
    此对于全局停顿情况的发生也能得到较好的控制
    随着 G1的出现,GC 从传统的连续堆内存布局主键走向了不连续内存块布局,这是通过引入 Region 概念
    实现的,也就是说由一堆不连续的 Region 组成了堆内存,当然并不是真的不连续,只是由原先的物理连续
    主键转变为逻辑连续,通过 Region 动态分配可以把一个 Region 分配给 Eden 或者 Survivor 或者老
    年代或者大对象区间或者空闲区间等任意一个
    在JDK1.9中移除了JDK 8中已弃用的垃圾收集器(GC)组合,官网原始文档如下:
    Removes garbage collector (GC) combinations that were deprecated in JDK 8.
    This means that the following GC combinations no longer exist:
  15. DefNew + CMS
  16. ParNew + SerialOld
  17. Incremental CMS
    The “foreground” mode for Concurrent Mark Sweep (CMS) has also been
    removed. The following command-line flags have been removed:
  18. -Xincgc
    QF-JAVA
  19. -XX:+CMSIncrementalMode
  20. -XX:+UseCMSCompactAtFullCollection
  21. -XX:+CMSFullGCsBeforeCompaction
  22. -XX:+UseCMSCollectionPassing
    The command line flag -XX:+UseParNewGC no longer has an eect. ParNew can only be used with
    CMS and CMS requires ParNew. Thus, the -XX:+UseParNewGC flag has been deprecated and will likely
    be removed in a future release.
    在JDK1.9以后,将不再推荐使用CMS垃圾回收器,官方推荐使用 G1 垃圾回收器,官方原始文档如下:
    Makes Garbage-First (G1) the default garbage collector (GC) on 32- and 64-bit >server configurations.
    Using a low-pause collector such as G1 provides a better >overall experience, for most users, than a
    throughput-oriented collector such as >the Parallel GC, which was previously the default.
    回收机制
    G1 GC 的垃圾回收循环主要由三个主要类型组成 : 年轻代循环 , 多步骤并行标记循环 混合收集循
    环 ,当然FULL GC 还是存在的,它主要是对 GC 的评估失败提供失败保护机制,完成强力回收
    G1 GC 在回收暂停阶段会回收最大量的堆内区间(Region),通过回收区间达到回收垃圾的目的,但是
    在并行标记阶段清除的时候会发生例外,G1 GC 在清除步骤发现如果所有的区间都是由可回收垃圾
    组成的,那么它会立即回收这些区间,并将这些区间插入一个基于 LinkedList 实现的空闲区间队列里,
    以备后用,因此释放这些区间并不需要等到下一个垃圾回收的中断,它是实时的
    在年轻代回收期,G1 GC 会暂停应用线程,然后从年轻代区间移动存活对象到幸存者区间或者老年代区
    间,也有可能两个区间都涉及,对于一个混合回收,G1 GC 从老年代区间移动存活对象到空间区间,这些空间
    区间也就成了老年代的一部分
    在 G1 中,堆内存被平均分成若干个大小相等的区域(Region),每个 Region 都有一个关联的
    Remembered Set(RSet),RSet 是 Hash表,里面多数据是 Card Table(堆中每512byte 映射在 card table 1byte),
    简单的说,Rset 里面存在的是在 Region 存活的对象的指针,当 Region 中的数据发生变化的时候,首先反应
    到 Card Table 中的一个或者多个 Card 上面 RSet 通过扫描内部的 Card Table 来得到 Region 中内存的使用
    情况和存活对象,在使用 Region的过程中,如果 Region 填满了,分配内存的线程就会重新选择一个可用的新
    的 Region, 空闲的 Region 被存在一个基于LinkedList 的结构中,这样可以快速获取到新 Region
    G1 GC 分代管理
    G1 GC收集过程涵盖了4个阶段,即年轻代 GC,并发标记周期,混合收集和 Full GC
    年轻代
    G1 GC 依然是一个基于分代概念涉及的 GC,年龄代由年轻代和年老代组成,对于绝大多数正常情况
    下的对象分配方式而言,他们都会被分配在线程的本地分配缓存(TLAB)里面.如果对象必 TLAB 所能存
    储的对象更大一些,这些对象会被当成大对象(Humongous) 相应的存储到大对象区间(Humongous
    Regions),当一个对象比大对象小一点,又大于 TLAB 的大小,那么会有特定的线程区处理这些数据
    因为 TLAB 调用资源使用的是本地 Java 线程的无锁分配模式,所以对象的分配速度很快,这些 TLAB
    来自于 G1区间并被加入到年轻代
    年轻代回收暂停
    年轻代是由 Eden 区和 Survivor 两个区间组成,当 Eden 区间分配内存失败,也可以说内存占满的
    时候,一次年轻代回收就被触发了,这次的触发对于 GC 来说,它的工作是释放一些内存,属于一次轻量
    级的回收操作,首先,GC 需要吧所有的存活对象从 Eden 区间移动到 Survicor 区域,这就是所谓的拷贝
    到幸存者区间都动作,这个操作类似于交换算法操作,任何一个年轻代回收都会提升整个年轻代的存
    QF-JAVA
    活对象(有 Eden 和 Survivor区间组成)到新的 Survior 区间
    当存活对象从年轻代被移动到年老代的时候,我们成为对象的提升.也可以称为对象进阶,相应的
    这个进阶是有年龄限制的,默认情况下,在年轻代中的对象每经过一次回收并存活下来年龄就加1,当
    年龄到达15的时候就会被提升到老年代
    大对象区间
    在G1内部,堆区间大小决定了什么对象可以被认定为大对象,大对象通常是很大的对象, 如果大小
    超过了 Region 的50%,则认为是一个大对象,大对象没有按照年轻代等会收方式把对象放到老年代,而
    是放到了老年代以外的大对象区间里面,即单独分离出来一片新的区域,独立管理
    大对象区间独立的原因是因为大对象在堆内存里面是物理连续的,所以在回收阶段如果频繁的回
    收大对象,存在两个问题:1.移动对象需要拷贝数据,对象比较大,拷贝效率会成为瓶颈,2就是很难找到
    连续的堆内存用于大对象存储, 回收越频发,越容易出现相互独立的空间,所以大对象是单独划分的
    在 JDK8U40之前,即便大对象区间是完全空闲的,也只会在并行回收循环的清除暂停阶段才会回
    收大对象,而在之后的版本,新增了年轻代,FULL GC 阶段针对大对象区间的回收功能,只要大对象不在
    使用,这些区间就会被回收并且放入空间空间队列
    混合回收暂停
    成功完成并发标记周期后, g1 gc 从执行年轻代回收切换到执行混合回收。在混合回收中, g1 gc 可
    选择将一些老年代区域添加到将回收的 Eden 和幸存者区域集。添加的老年区域的确切数量由几个
    flag控制。g1 gc 回收足够数量的老年代区域 (通过多次混合回收) 后, g1 将恢复到执行年轻代回收,
    直到下一个并发标记周期完成。
    那么怎么决定把那些老年区region回收呢?这就是并发标记周期concurrent marking cycle要做的,
    它分为五个步骤:
    (1) 初始标记 (initial mark,STW)
    在此阶段,G1 GC 对根进行标记。该阶段主要利用了常规的年轻代垃圾回收暂停(STW)。
    (2) 根区域扫描 (root region scan)
    G1 GC 在初始标记的年轻代存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用
    程序(非 STW)同时运行,并且只有完成该阶段后,才能开始下一次 STW 年轻代垃圾回收。
    (3) 并发标记 (Concurrent Marking)
    G1 GC 在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW 年
    轻代垃圾回收中断。
    (4) 最终标记 (Remark,STW)
    该阶段是 STW 回收,帮助完成标记周期。G1 GC 清空 SATB 缓冲区,跟踪未被访问的存活对
    象,并执行引用处理。
    (5) 清除垃圾 (Cleanup,STW)
    我们知道年轻代回收是把年轻代活着的对象都拷贝到Survivor的特定区域(Survivor to),剩下的Eden和
    Survivor from就可以全部回收清理了。那么,mixed GC就是把一部分老年区的region加到Eden和
    Survivor from的后面,合起来称为collection set, 就是将被回收的集合,下次mixed GC
    evacuation把他们所有都一并清理。选old region的顺序是垃圾多的(存活对象少)优先,这也是
    garbage first的来历。这也是利用了G1堆逻辑分区(Region)的灵活性。G1似乎并没有把Survivor分成
    from和to,但是回收时的道理是一样的,暂且这么理解。
    QF-JAVA
    在这个最后阶段,G1 GC 执行统计和 RSet 净化的 STW 操作。在统计期间,G1 GC 会识别完全空的
    区域和可供进行混合垃圾回收的区域。清理阶段在将空白区域重置并添加到空闲列表时为部分并
    发。注意完全空的region不会被加到CSet,都在这个阶段直接回收了。
    观察:
    1.从(3)开始并发标记周期这个过程可以和年轻代回收并发,当然看起来年轻代回收会打断
    (3),(4)和(5)会暂停所有访问堆的线程(STW)。
    2.混合回收就是把老年代的一些区域加在将要回收的Eden和survivor的后面,这些所有将要回收的
    区域叫做Collection Set(CSet),最后用年轻代回收算法一起回收。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值