JVM - 垃圾判断算法
JVM 解密 —— 垃圾判断算法
在 JVM 对堆内存进行回收之前,首先要解决一个问题:如何判断哪些对象是“垃圾”(即不再被任何地方使用的对象)?
主要有两种经典的判断算法:引用计数算法和可达性分析算法。
1. 深度剖析:两大判断算法
1.1 引用计数算法 (Reference Counting)
-
原理: 在对象中添加一个引用计数器。每当有一个地方引用它时,计数器值就加 1;当引用失效时,计数器值就减 1。任何时刻当计数器为 0 的对象,就是不可能再被使用的,可以被回收。
-
优点: 实现简单,判断效率高。大部分情况下,它是一个不错的算法。
-
致命缺点: 无法解决对象之间循环引用的问题。如果两个或多个对象相互引用,即使它们在程序中已经没有任何其他地方使用了,但它们的引用计数器永远不为 0,导致它们永远无法被回收,造成内存泄漏。
-
生活比喻: 想象每个人(对象)头上都有一个数字,代表“有多少人还惦记着你”。
- 小明被 A、B 两个人惦记,他的计数值就是 2。
- 后来 A、B 都不再惦记他了,他的计数值变为 0,说明他“没用了”,可以被“清理”了。
- 循环引用问题:现在,小红和小刚两个人,除了彼此,谁也不惦记。小红只惦记小刚,小刚只惦记小红。于是他们俩头上的计数值都是 1。虽然在外界看来,这两个人已经“与世隔绝”,毫无用处,但因为他们互相惦记,导致计数值永远不为 0,系统就没法把他们清理掉。
结论:由于存在循环引用的硬伤,现代主流的 Java 虚拟机(如 HotSpot)并未使用引用计数算法来管理内存。
1.2 可达性分析算法 (Reachability Analysis)
这是当前主流虚拟机(包括 HotSpot)正在使用的算法。
-
原理: 通过一系列被称为“GC Roots”的根对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链 (Reference Chain)。当一个对象到任何 GC Roots 之间没有任何引用链相连时(用图论的话说,就是从 GC Roots 到这个对象不可达),则证明此对象是不可用的,可以被回收。
-
什么是 GC Roots: 在 Java 语言中,可作为 GC Roots 的对象包括(但不限于)以下几种:
- 虚拟机栈中引用的对象(即当前正在执行的方法里的局部变量所引用的对象)。
- 也就是当前正在执行的方法中,所有的局部变量所引用的对象。只要方法还在执行,这些对象就必须存活。
- 方法区中类静态属性引用的对象。
- 类中由static关键字修饰的字段所引用的对象。这些字段属于类本身,与类的生命周期绑定,因此它们引用的对象是存活的。
- 方法区中常量引用的对象。
- 字符串常量池中的引用,或者由final static修饰的常量所引用的对象。
- 本地方法栈中 JNI (即一般说的 Native 方法) 引用的对象。
- 当Java代码调用C或C++等本地代码时,本地代码可能会持有Java对象的引用,这些对象也必须存活。
- 被同步锁(
synchronized)持有的对象。- 正在被用于synchronized锁定的对象,它们不能被回收。
- Java虚拟机内部的引用。
- 如基本数据类型对应的Class对象,一些常驻的异常对象(如NullPointerException, OutOfMemoryError)等,还有系统类加载器。
- 虚拟机栈中引用的对象(即当前正在执行的方法里的局部变量所引用的对象)。
-
优点: 完美地解决了循环引用的问题。在上面的例子中,小红和小刚虽然互相引用,但他们与任何一个“根人物”(GC Roots)都没有关系,所以从根出发,无论如何也找不到他们。因此,他们俩都会被判定为“不可达”的垃圾,可以被一起回收。
-
生活比喻: 想象一次“寻人启事”。
- GC Roots: 就是一群“根正苗红”的、肯定不是失踪人口的“核心人物”,比如正在上班的员工(虚拟机栈)、公司的创始人(静态属性)等。
- 可达性分析: 从这些核心人物出发,去寻找他们的亲戚、朋友、同事……所有能通过这条“关系链”找到的人,都被认为是“活人”(存活对象)。
- 垃圾对象: 经过一轮寻找后,那些没有任何关系链能联系到的“孤魂野鬼”(比如上面与世隔绝的小红和小刚),就被认为是“失踪人口”(垃圾对象),可以被安全地清理掉。
2. 扩展知识:Java 的四种引用类型
在 JDK 1.2 之后,Java 对引用的概念进行了扩充,将引用分为四种,这使得程序能更加灵活地控制对象的生命周期。
-
强引用 (Strong Reference):
- 定义: 就是我们最常用的
Object obj = new Object();这种引用。 - 特点: 只要强引用还存在,垃圾收集器永远不会回收被引用的对象,即使内存不足时,宁愿抛出
OutOfMemoryError。
- 定义: 就是我们最常用的
-
软引用 (Soft Reference):
- 定义: 用
SoftReference类来实现。 - 特点: 只有在内存不足时,垃圾收集器才会回收这些对象。因此,软引用非常适合用来实现高速缓存。
- 定义: 用
-
弱引用 (Weak Reference):
- 定义: 用
WeakReference类来实现。 - 特点: 只要垃圾收集器运行,无论当前内存是否充足,都会回收掉只被弱引用关联的对象。
WeakHashMap的 Key 就是弱引用,当 Key 被回收后,对应的 Value 也会被自动移除。
- 定义: 用
-
虚引用 (Phantom Reference):
- 定义: 用
PhantomReference类来实现,也称为“幽灵引用”或“幻影引用”。 - 特点: 它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。它的唯一作用就是能在这个对象被收集器回收时收到一个系统通知,通常用于管理堆外内存。
- 定义: 用

浙公网安备 33010602011771号