GC 垃圾回收机制
创始人
2025-05-31 23:07:29
0

文章目录

    • JVM 的内存模型
    • 对象存活?
      • 引用计数算法
      • 可达性分析算法
    • 垃圾收集
      • 标记-清除算法
      • 标记-复制算法
      • 标记-整理算法
    • 垃圾收集器
      • 垃圾收集器发展
      • Serial / Serial Old
      • Parallel Scavenge / Parallel Old
      • ParNew / CMS
      • G1
      • ZGC
    • 扩展

JVM 的内存模型

Java 虚拟机(Java Virtual Machine,简称 JVM)根据《Java 虚拟机规范》的规定,Java 虚拟机所管理的内存将会包括以下几个运行时数据区域,如图所示:

对于 Java 应用程序来说,其中 Java 堆(Java Heap)和方法区(元空间或者永久代)是虚拟机所管理的内存中最大的一块。而垃圾回收机制所关注的正是这部分内存该如何管理。

其中方法区垃圾收集的“性价比”通常也是比较低的;在 Java 堆中,尤其是在新生代中,对常规应用进行一次垃圾收集通常可以回收 70%至 99%的内存空间,相比之下,方法区的垃圾收集的回收成果往往远低于此。同时《Java 虚 拟机规范》中提到过可以不要求虚拟机在方法区中实现垃圾收集,事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在(如 JDK 11 中的 ZGC 收集器就不支持类卸载)。

在 Java 中,垃圾回收机制(Garbage Collection,简称 GC)是一个非常重要的概念,它主要的作用是回收程序当中不再使用的内存。因此 GC 要负责完成 3 项任务:分配内存,确保被引用对象的内存不被错误地回收,回收不再被引用的对象的内存。

对象存活?

在 Java 堆里面存放着几乎所有的对象实例,GC 在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(“死去”即不可能再被任何途径使用的对象)了,对于“死去”的对象我们会将其标记为垃圾,之后 GC 会将这些进行标记的进行回收。判断对象是否“存活”的算法主要为两种:引用计数算法,可达性分析算法。

引用计数算法

引用计数法通过在堆中对每个对象都有一个引用计数器;当对象被引用时,计数器值就加 1;当引用被置空或者离开作用域时,计数器值就减 1:任何时刻计数器为零的对象就是不可能再被使用的。

引用计数算法虽然简单,但是如果出现两个对象之间互相引用时,两个对象都是垃圾,但是计数器值永远不为 0,GC 就无法对其回收。因此 Java 使用了可达性分析算法来判定对象是否存活的。

可达性分析算法

可达性分析算法就是通过一系列称为“GC Roots’的根对象作为起始节点,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象不可达时,意味着此对象是不可能再被使用的,就被判定为垃圾。

Java 中固定可作为 GC Roots 的对象包括以下几种:

  • 栈中引用的对象;
  • 类静态属性引用的对象;
  • 常量引用的对象;
  • Native 本地方法中引用的对象;

垃圾收集

首先,基于弱分代假说和强分代假说形成了分代收集理论:

  • 弱分代假说(Weak Generational Hy pothesis):绝大多数对象都是朝生夕灭的。
  • 强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡。

根据分代收集理论一般会把 Java 堆划分为新生代(Young Generation)和老年代(Old Generation)两个区域。针对 GC 对回收其中某一个或者某些部分的区域,又划分为“Minor GC"Major GC"和“Full GC 这样的回收类型

  • 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。
  • 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有 CMS 收集器会有单独收集老年代的行为。由于一般 Major GC 发生的时候通常也会伴随着 Minor GC,所以 Major GC 也经常被称为 Full GC 或 FGC,也就是全局范围的 GC 动作。
  • 整堆收集(Full GC):收集整个 Java 堆和方法区的垃圾收集。

标记-清除算法

“标记-清除”(Mark-Sweep)算法分为“标记和“清除两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。

当垃圾收集器将内存扫描之后会标记出所有垃圾对象然后将它们回收,但这个方法有一个缺点,就是会不断的会产生大量不连续的内存碎片,使内存的使用率变得越来越低,也可能会导致以后程序无法为大对象分配一片连续的内存空间。

标记-复制算法

标记-复制算法常被简称为复制算法。为了解决标记-清除算法面对大量可回收对象时执行效率低的问题,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当第一块的内存剩余不足时,会将所有需要保留的对象复制到另外一块内存,然后再把已使用过的内存直接清空。

标记-复制算法既做到了垃圾回收,又做到了碎片整理,但代价是将可用内存缩小为了原来的一半,内存的空间浪费一倍。

标记-整理算法

标记-整理(Mak-Compact)算法的标记过程与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。简单来说就是在清理垃圾的基础上,增加了一步碎片整理的操作。

垃圾收集器

垃圾收集器发展

垃圾收集器根据迭代版本分为:

  • 早期的 Serial 和 Serial Old;
  • 中期的 Parallel Scavenge 和 Parallel Old;
  • 过渡期的 ParNew 和 CMS;
  • JDK9 默认垃圾收集器 G1(Garbage First);
  • JDK11 中新加入的低延迟垃圾收集器 ZGC(Z Garbage Collector)。

Serial / Serial Old

Serial 是一个工作在新生代的单线程收集器,与它搭档负责老年代的垃圾收集器为 Serial Old。单线程意味着在进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束,这个动作习惯称为 STW(Stop The World)。Serial/Serial Old 收集器的运行过程如图:

Parallel Scavenge / Parallel Old

Parallel Scavenge 是多线程的收集器,与它搭档的是 Parallel Old。不同于 Serial/Serial Old 收集器的是,当开始垃圾回收时,所有用户线程必须全部暂停,依然触发了 STW,不过这次垃圾回收变成了多线程,对于多 CPU 的服务器来讲,提高了不少效率,不过依然无法避免 STW。

ParNew / CMS

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,与它搭档工作在新生代的垃圾收集器为 ParNew,与 Parallel Scavenge 基本相同。CMS 收集器是基于标记-清除算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为四个阶段,括:

  1. 初始标记(CMS initial mark)
  2. 并发标记(CMS concurrent mark)
  3. 重新标记(CMS remark)
  4. 并发清理(CMS concurrent sweep)

其中初始标记、重新标记这两个步骤仍然会触发 STW。初始标记仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快;并发标记阶段就是从 GC Roots 的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行;而重新标记阶段则是为了修正并发标记期间的错标问题,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短;最后是并发清除阶段,清理删除掉标记阶段判断的己经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。CMS 的并发工作流程如下:

G1

G1 是一款主要面向服务端应用的垃圾收集器。JDK9 中,G1 取代 Parallel Scavenge 加 Parallel Old 组合,成为服务端模式下的默认垃圾收集器。G1 允许用户手动设置一个期望的 STW 时间。

G1 基于 Region 的堆内存布局,即 G1 不再坚持固定大小以及固定数量的分代区域划分,而是把连续的 Java 堆划分为多个大小相等的独立区域(Region,Region 的大小可以通过参数-XX:G1Heap RegionSize设定,取值范围为 1MB~32MB,且应为 2 的 N 次幂),每一个 Region 都可以根据需要,扮演新生代的 Ede 空间、Survivor 空间,或者老年代空间,而新生代和老年代的空间大小不再是绝对固定,而且当 GC 扫描内存时,无需扫描整块内存,只需扫描特定区域即可,极大的提高了它所能支持的堆内存的大小

Region 中还有一类特殊的 Humongous 区域,专门用来存储大对象。G1 认为只要大小超过了一个 Region 容量一半的对象即可判定为大对象。每个 Region 的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围为 1MB~32MB,且应为 2 的 N 次幂。而对于那些超过了整个 Region 容量的超级大对象,将会被存放在 N 个连续的 Humongous Region 之中,直至被回收之前,在内存的位置始终保持不变,避免了对老年代整理时频繁的移动大对象。

ZGC

ZGC 是一款在 JDK 11 中新加入的具有实验性质的低延迟垃圾收集器,在 JDK 15 中正式投入生产使用了,使用 –XX:+UseZGC 命令可以启用 ZGC。

ZGC 与 G1 一样,也采用了基于 Region 的堆内存布局,但与不同的是,ZGC 没有分代的概念,而且 Region 具有动态性——动态创建和销毁,以及区域容量大小也是动态的。

在 x64 硬件平台下,ZGC 的 Region 可以具有大、中、小三类容量:

  • 小型 Region(Small Region):容量固定为 2MB,用于放置小于 256KB 的小对象。
  • 中型 Region(Medium Region):容量固定为 32MB,用于放置大于等于 256KB 但小于 4MB 的对象。
  • 大型 Region(Large Region):容量不固定,可以动态变化,但必须为 2MB 的整数倍,用于放置 4MB 或以上的大对象。每个大型 Region 中只会存放一个大对象,这也预示着虽然名字叫作“大型 Region”,但它的实际容量完全有可能小于中型 Region,最小容量可低至 4MB。

ZGC 运作过程如图:

ZGC 做到了几乎整个收集过程都全程可并发,短暂停顿也只与 GC Roots 大小相关而与堆内存大小无关,因而实现了任何堆上停顿都小于十毫秒的目标。

扩展

在命令行中使用以下命令查看 JDK 使用的垃圾收集器:

java -XX:+PrintCommandLineFlags -version

img

相关内容

热门资讯

最绚丽的安卓系统,最绚丽版本全... 哇,你知道吗?在安卓的世界里,有一款系统,它就像是一颗璀璨的明珠,闪耀着最绚丽的色彩。它就是——最绚...
小米系统安卓通知权限,深度解析... 亲爱的手机控们,你是否曾为手机通知栏里乱糟糟的信息而烦恼?又或者,你是否好奇过,为什么有些应用总是能...
安卓7.0系统能玩吗,体验全新... 你有没有想过,你的安卓手机升级到7.0系统后,那些曾经陪伴你度过无数时光的游戏,还能不能继续畅玩呢?...
平板安卓系统哪家好,安卓平板系... 你有没有想过,在这个科技飞速发展的时代,拥有一台性能出色的平板电脑是多么重要的事情呢?想象无论是追剧...
安卓好的点歌系统,打造个性化音... 你有没有想过,在安卓手机上,点歌系统竟然也能如此精彩?没错,就是那个我们每天都会用到,却又常常忽略的...
熊猫安卓系统直播软件,解锁互动... 你知道吗?最近有个超级酷炫的直播软件在熊猫迷们中间火得一塌糊涂!它就是熊猫安卓系统直播软件。别看它名...
安卓点播系统开发,Androi... 你有没有想过,手机里那些让你爱不释手的视频,其实背后有着一套复杂的安卓点播系统在默默支撑呢?今天,就...
安卓6.0系统加权限,深度解析... 你有没有发现,自从手机升级到安卓6.0系统后,权限管理变得超级严格呢?这可真是让人又爱又恨啊!今天,...
哪些电视带安卓系统,多款热门智... 你有没有想过,家里的电视竟然也能装上安卓系统?听起来是不是有点不可思议?没错,现在市面上就有不少电视...
苹果怎么运用安卓系统,揭秘如何... 你知道吗?最近有个大新闻在科技圈里炸开了锅,那就是苹果竟然开始运用安卓系统了!是不是觉得有点不可思议...
安卓系统能转什么系统好,探索最... 你有没有想过,你的安卓手机是不是也能换换口味,体验一下其他系统的魅力呢?没错,今天就来聊聊这个话题:...
龙之狂热安卓系统,释放龙族狂热 亲爱的手机控们,你是否曾为拥有一款独特的安卓系统而疯狂?今天,就让我带你走进一个充满奇幻色彩的龙之狂...
vivo手机安卓系统怎么升级系... 亲爱的手机控们,你是不是也和我一样,对手机的新功能充满期待呢?尤其是vivo手机的用户,是不是也在想...
鸿蒙2.0退回安卓系统,一场系... 你知道吗?最近科技圈里可是炸开了锅,因为华为的鸿蒙2.0操作系统竟然要退回安卓系统了!这可不是一个简...
安卓系统怎么复制卡,安卓系统卡... 你有没有遇到过这种情况:手机里的照片、视频或者重要文件,突然想备份到电脑上,却发现安卓系统的卡复制功...
app兼容低安卓系统,打造全民... 你有没有发现,现在手机APP更新换代的速度简直就像坐上了火箭!不过,你知道吗?有些APP可是特别贴心...
中间安卓系统叫什么,中间安卓系... 你有没有想过,安卓系统里竟然还有一个中间的版本?没错,就是那个让很多手机用户既熟悉又陌生的版本。今天...
安卓怎么用os系统,利用And... 你有没有想过,你的安卓手机其实可以变身成一个功能强大的操作系统呢?没错,就是那个我们平时在电脑上使用...
pe系统安卓能做么,探索安卓平... 亲爱的读者,你是否曾好奇过,那款在安卓设备上大受欢迎的PE系统,它究竟能做什么呢?今天,就让我带你一...
安卓 打印机系统,安卓打印机系... 你有没有想过,家里的安卓手机和打印机之间竟然能建立起如此紧密的联系?没错,就是那个安卓打印机系统!今...