JVM 垃圾回收的工作原理
创始人
2024-03-02 11:34:05
0

对于程序员来说,掌握 Java 的内存管理机制并不是必须的,但它能够帮助你更好地理解 JVM 是如何处理程序中的变量和类实例的。

Java 之所以能够如此流行,自动 垃圾回收 Garbage Collection (GC)功不可没,它也是 Java 最重要的几个特性之一。在这篇文章中,我将说明为什么垃圾回收如此重要。本文的主要内容为:自动的分代垃圾回收、JVM 划分内存的依据,以及 JVM 垃圾回收的工作原理。

Java 内存分配

Java 程序的内存空间被划分为以下四个区域:

  1. 堆区 Heap :对象实例就是在这个区域分配的。不过,当我们声明一个对象时,堆中不会发生任何内存分配,只是在栈中创建了一个对象的引用而已。
  2. 栈区 Stack :方法、局部变量和类的实例变量就是在这个区域分配的。
  3. 代码区 Code :这个区域存放了程序的字节码。
  4. 静态区 Static :这个区域存放了程序的静态数据和静态方法。

什么是自动垃圾回收?

自动垃圾回收是这样一个过程:首先,堆中的所有对象会被分类为“被引用的”和“未被引用的”;接着,“未被引用的对象”就会被做上标记,以待之后删除。其中,“被引用的对象”是指程序中的某一部分仍在使用的对象,“未被引用的对象”是指目前没有正在被使用的对象。

许多编程语言,例如 C 和 C++,都需要程序员手动管理内存的分配和释放。在 Java 中,这一过程是通过垃圾回收机制来自动完成的(尽管你也可以在代码中调用 system.gc(); 来手动触发垃圾回收)。

垃圾回收的基本步骤如下:

1、标记已使用和未使用的对象

在这一步骤中,已使用和未使用的对象会被分别做上标记。这是一个及其耗时的过程,因为需要扫描内存中的所有对象,才能够确定它们是否正在被使用。

标记已使用和未使用的对象

2、扫描/删除对象

有两种不同的扫描和删除算法:

简单删除(标记清除):它的过程很简单,我们只需要删除未被引用的对象即可。但是,后续给新对象分配内存就会变得很困难了,因为可用空间被分割成了一块块碎片。

标记清除的过程

删除压缩(标记整理):除了会删除未被引用的对象,我们还会压缩被引用的对象(未被删除的对象)。这样以来,新对象的内存分配就相对容易了,并且内存分配的效率也有了提升。

标记整理的过程

什么是分代垃圾回收,为什么需要它?

正如我们在“扫描删除”模型中所看到的,一旦对象不断增长,我们就很难扫描所有未使用的对象以回收内存。不过,有一项实验性研究指出,在程序执行期间创建的大多数对象,它们的存活时间都很短。

既然大多数对象的存活时间都很短,那么我们就可以利用这个事实,从而提升垃圾回收的效率。该怎么做呢?首先,JVM 将内存划分为不同的“代”。接着,它将所有的对象都分类到这些内存“代”中,然后对这些“代”分别执行垃圾回收。这就是“分代垃圾回收”。

堆内存的“代”和分代垃圾回收过程

为了提升垃圾回收中的“标记清除”的效率,JVM 将对内存划分成以下三个“代”:

  • 新生代 Young Generation
  • 老年代 Old Generation
  • 永久代 Permanent Generation

Hotspot 堆内存结构

下面我将介绍每个“代”及其主要特征。

新生代

所有创建不久的对象都存放在这里。新生代被进一步分为以下两个区域:

  1. 伊甸区 Eden :所有新创建的对象都在此处分配内存。
  2. 幸存者区 Survivor ,分为 S0 和 S1:经历过一次垃圾回收后,仍然存活的对象会被移动到两个幸存者区中的一个。

对象分配

在新生代发生的分代垃圾回收被称为 “ 次要回收 Minor GC ”(LCTT 译注:也称为“ 新生代回收 Young GC ”)。Minor GC 过程中的每个阶段都是“ 停止世界 Stop The World ”(STW)的,这会导致其他应用程序暂停运行,直到垃圾回收结束。这也是次要回收更快的原因。

一句话总结:伊甸区存放了所有新创建的对象,当它的可用空间被耗尽,第一次垃圾回收就会被触发。

填充伊甸区

次要回收:在该垃圾回收过程中,所有存活和死亡的对象都会被做上标记。其中,存活对象会被移动到 S0 幸存者区。当所有存活对象都被移动到了 S0,未被引用的对象就会被删除。

拷贝被引用的对象

S0 中的对象年龄为 1,因为它们挺过了一次次要回收。此时,伊甸区和 S1 都是空的。

每当完成清理后,伊甸区就会再次接受新的存活对象。随着时间的推移,伊甸区和 S0 中的某些对象被宣判死亡(不再被引用),并且伊甸区的可用空间也再次耗尽(填满了),那么次要回收 又将再次被触发。

对象年龄增长

这一次,伊甸区和 S0 中的死亡和存活的对象会被做上标记。其中,伊甸区的存活对象会被移动到 S1,并且年龄增加至 1。S0 中的存活对象也会被移动到 S1,并且年龄增加至 2(因为它们挺过了两次次要回收)。此时,伊甸区和 S0 又是空的了。每次次要回收之后,伊甸区和两个幸存者区中的一个都会是空的。

新对象总是在伊甸区被创建,周而复始。当下一次垃圾回收发生时,伊甸区和 S1 都会被清理,它们中的存活对象会被移动到 S0 区。每次次要回收之后,这两个幸存者区(S0 和 S1)就会交换一次。

额外年龄增长

这个过程会一直进行下去,直到某个存活对象的年龄达到了某个阈值,然后它就会被移动到一个叫做“老年代”的地方,这是通过一个叫做“晋升”的过程来完成的。

使用 -Xmn 选项可以设置新生代的大小。

老年代

这个区域存放着那些挺过了许多次次要回收,并且达到了某个年龄阈值的对象。

晋升

在上面这个示例图表中,晋升的年龄阈值为 8。在老年代发生的垃圾回收被称为 “ 主要回收 Major GC ”。(LCTT 译注:也被称为“ 全回收 Full GC ”)

使用 -Xms-Xmx 选项可以分别设置堆内存大小的初始值和最大值。(LCTT 译注:结合上面的 -Xmn 选项,就可以间接设置老年代的大小了。)

永久代

永久代存放着一些元数据,它们与应用程序、Java 标准环境以及 JVM 自用的库类及其方法相关。JVM 会在运行时,用到了什么类和方法,就会填充相应的数据。当 JVM 发现有未使用的类,就会卸载或是回收它们,从而为正在使用的类腾出空间。

使用 -XX:PermGen-XX:MaxPerGen 选项可以分别设置永久代大小的初始值和最大值。

元空间

Java 8 引入了 元空间 Metaspace ,并用它替换了永久代。这么做的好处是自动调整大小,避免了 内存不足 OutOfMemory (OOM)错误。

总结

本文讨论了各种不同的 JVM 内存“代”,以及它们是如何在分代垃圾回收算法中起作用的。对于程序员来说,掌握 Java 的内存管理机制并不是必须的,但它能够帮助你更好地理解 JVM 处理程序中的变量和类实例的方式。这种理解使你能够规划和排除代码故障,并理解特定平台固有的潜在限制。

正文配图来自:Jayashree Huttanagoudar,CC BY-SA 4.0


via: https://opensource.com/article/22/6/garbage-collection-java-virtual-machine

作者:Jayashree Huttanagoudar 选题:lkxed 译者:lkxed 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

相关内容

Edge.js发布:为AI...
Wasmer公司推出了Edge.js,这是一个基于WebAssem...
2026-03-26 01:38:53
AI编程助手飞算JavaA...
钛媒体App 1月27日消息,获中国信通院认证生成完整工程代码的A...
2026-01-27 10:49:28
学java要学linux吗...
嘿,朋友们!今天咱们来聊聊一个很多Java新手都会纠结的问题:学j...
2026-01-22 09:49:54
Java、Python等开...
IT之家 9 月 24 日消息,开源安全基金会(OpenSSF)昨...
2025-09-24 14:25:34
怎么用AI做短视频赚钱?小...
嘿,你是不是也刷到过那些爆火的短视频,心想:“这玩意儿咋这么赚钱?...
2025-06-06 22:12:18
JAVA集合知识整理
Java集合知识整理 HashMap相关 HashMap的底层数据...
2025-06-01 22:12:08

热门资讯

“Linux 中国” 开源社区... 这是一个艰难的决定 —— 这不是调侃,而是当你真正做出改变了你十几年生活轨迹的决定时,你就会明白这个...
硬核观察 #1263 德国铁路... #1 德国铁路公司招聘 Windows for Workgroups 3.11 管理员该职位要求了解...
人工智能教程(七):Sciki... 在本系列的 上一篇文章 中,我们用 TensorFlow 构建了第一个神经网络,然后还通过 Kera...
关闭 WSL 中正在运行的 L... 以下是使用 WSL 关闭在 Windows 内运行的 Linux 系统的不同方法。你使用 WSL 在...
MX Linux 23.2 “... 最新的 MX Linux 23.2 小版本更新带来了基本的改进和新增功能。MX Linux 推出了 ...
硬核观察 #1262 网络上最... #1 网络上最古老的软件存档库之一即将关闭美国新墨西哥州立大学(NMSU)最近宣布,即将于 2024...
如何在 RHEL 上通过绑定配... 在 Linux 中,你可以根据自己的需要创建高级网络设置,如 网络绑定 Network Bondin...
Linux Matrix 消息... Matrix 用户可以升级 Fractal 6 了!Fractal 是目前 最好的 Matrix 客...
如何通过 VLC 使用字幕 使用 VLC 媒体播放器播放和管理字幕的新手指南。我是一个超级动漫迷,如果你想观看最新的剧集,那么你...
硬核观察 #1261 X 计划... #1 X 计划在奥斯汀建立内容审核总部据报道,该部门将招聘 100 名全职员工,将主要关注 CSE ...