JVM 的可达性分析是 Java 虚拟机自动进行垃圾回收的一种技术,其基本思路是通过一系列的“GC Roots”对象作为起始点,从这些根对象开始向下搜索,搜索到的对象称为“可达对象”,而没有搜索到的对象则认为是“不可达对象”,即可以进行垃圾回收。
“GC Roots”对象
JVM 中的“GC Roots”对象包括以下几种:
- 虚拟机栈中的引用对象:函数内的局部变量、参数、异常处理器等
- 方法区中的类静态属性引用的对象
- 方法区中的常量引用的对象
- 本地方法栈中JNI(Java Native Interface)引用的对象
下面以示例代码来说明 JVM 的可达性分析过程:
public class Person {
private String name;
private Integer age;
private Person parent;
// getters and setters
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
p1.setParent(p2);
p2.setParent(p3);
p3.setParent(p1);
p1 = null;
p2 = null;
p3 = null;
}
}
在这个示例中,有三个 Person
对象 p1
、p2
和 p3
,并且它们之间形成了循环引用的关系。在 Main
类的 main
方法结束之后,这三个对象将变成不可达对象,可以进行垃圾回收。
JVM 的可达性分析过程
- 在程序最开始时,JVM 创建了一个名为“主线程”的线程,这个线程是 Java 应用程序的入口点。
- 在
main
函数执行前,JVM 在堆区中创建了三个Person
对象,并将它们的引用分别赋值给p1
、p2
和p3
。 - 进入
main
函数后,JVM 会分配一个栈桢,将该栈桢的引用指向main
函数的开始位置。 - 在
main
函数中,JVM 会为p1
、p2
和p3
分配空间,并将它们的地址压入栈桢中。 - 接下来,JVM 根据
p1
、p2
和p3
的引用,依次创建Person
对象,并将其地址保存到堆区中,同时更新栈桢中相应变量的值。 - 当
p1.setParent(p2)
执行时,JVM 将p2
的地址赋值给了p1
的parent
属性。 - 当
p2.setParent(p3)
执行时,JVM 将p3
的地址赋值给了p2
的parent
属性。 - 当
p3.setParent(p1)
执行时,JVM 将p1
的地址赋值给了p3
的parent
属性。 - 接下来,
p1
、p2
和p3
的引用分别被赋值为null
。 - 当
main
函数执行结束后,main
函数的栈桢出栈,Person
对象的引用也从栈桢中弹出,此时堆区中的p1
、p2
和p3
对象成为了不可达对象。 - JVM 的垃圾回收器会从根对象开始,对所有的可达对象进行遍历,将所有可达对象标记为“存活”,未被标记的对象视为垃圾,将被垃圾回收器清理掉。
在这个示例中,由于 main
函数是 JVM 的根对象之一,因此垃圾回收器会从 main
函数的栈桢开始遍历,发现 p1
、p2
和 p3
的引用已被赋值为 null
,因此这三个对象都是不可达对象,可以被垃圾回收器清理。
小故事
有一个小镇上有三个人,他们分别是John、Jane和Jim。他们的房子都是相邻的,每个人都有一把钥匙来打开自己的房门。
有一天,John去做了一次旅行,他忘记带钥匙了。在他离开之前,他把他的钥匙留给了Jane,说如果有人需要进他的房间,就让Jane拿走钥匙。而Jim则把他的钥匙放在了一张桌子上。
过了一段时间,Jane收到了一封信,信上说John已经回来了,他需要他的钥匙回来,Jane便把钥匙还给了John。而Jim却一直没人去过他那里,所以他的钥匙一直留在桌子上。
这个故事与JVM的可达性分析有些相似。在JVM中,对象之间存在一种引用关系,只有当一个对象不再被任何其他对象所引用时,它就变成了不可达对象,会被垃圾回收器回收。就像John授权给Jane使用他的钥匙,当Jane还钥匙给John时,表示John仍然需要这个钥匙使用,而Jim的钥匙没有人使用,因此可达性分析会认为Jim已经没有用了,会将他的钥匙回收。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?