58. Java 类和对象 - 垃圾回收器
🌿 引言
大家好!今天学习 Java 垃圾回收器 (Garbage Collector, GC)!
今天,我们要一起探讨 Java 是如何自动管理内存的,以及为什么垃圾回收机制是 Java 语言的一大亮点。
我们会围绕以下几个方面展开:
- 什么是垃圾回收器?
- 显式内存管理 vs. 自动垃圾回收
- 对象何时符合垃圾回收条件?
- 多引用的影响
- 垃圾回收器的工作机制
- 示例讲解
大家准备好了吗?那我们正式开始!🚀
🌱 1. 什么是垃圾回收器?
在某些面向对象语言中,比如 C++,我们需要手动跟踪对象,并在不需要时明确销毁它们,这种方式叫做 显式内存管理。
然而,这种方法有很大的风险:
- 如果你忘了释放内存,会导致 内存泄漏;
- 如果释放了还继续访问对象,会引发 空指针异常 等问题。
而 Java 提供了一种更安全和高效的方式:自动垃圾回收机制。
垃圾回收器 (Garbage Collector, GC) 是 Java 虚拟机 (JVM) 内置的工具。它会自动检测哪些对象不再被使用,并回收它们占用的内存。
换句话说,在 Java 中,你只需要专注于写代码,JVM 会帮你搞定大部分的内存管理工作!✨
⚔️ 2. 显式内存管理 vs. 自动垃圾回收
我们来快速对比一下:
| 特点 | 显式内存管理 (如 C++) | 自动垃圾回收 (Java) |
|---|---|---|
| 内存分配 | 手动调用 new | 自动调用 new |
| 内存释放 | 使用 delete 释放对象 | JVM 自动回收无引用对象 |
| 错误风险 | 内存泄漏、空指针异常 | 内存泄漏风险降低 |
| 控制力 | 精确控制对象何时销毁 | GC 自动决定回收时机 |
| 编程复杂度 | 需要额外管理内存逻辑 | 更简洁,无需手动释放内存 |
所以,Java 的自动垃圾回收让我们少踩很多内存管理的坑!✅
🏷️ 3. 对象何时符合垃圾回收条件?
Java 中,一个对象只有在 没有任何存活引用 时,才会被标记为 可回收对象。
让我们看看几种常见情况:
🎯 情况1:引用超出作用域
当方法结束后,局部变量引用会失效:
public void example() {
Point p = new Point(1, 2); // p在方法内有效
} // 方法结束后,p的引用消失,对象可回收
p 是一个局部变量,在方法执行完毕后就超出了作用域,因此与 Point 对象的关联被切断,Point 对象变成垃圾对象。
🎯 情况2:显式设置为 null
你也可以手动断开引用:
Point p = new Point(1, 2);
p = null; // p不再指向对象,该对象可回收
这里我们将 p 设置为 null,意味着不再引用任何对象,原本的 Point 对象会被回收。
🎯 情况3:引用被覆盖
当变量重新赋值时,原对象的引用会被覆盖:
Point p1 = new Point(1, 2);
Point p2 = p1; // p1 和 p2 指向同一个对象
p1 = new Point(3, 4); // p1 指向新对象
p2 = null; // 原对象无引用,可回收
这段代码中:
p1和p2最开始指向同一个对象Point(1, 2);- 当
p1被重新赋值为Point(3, 4)后,p2又被设为null; - 此时,
Point(1, 2)对象已没有任何引用,成为垃圾对象。
🔗 4. 多个引用的影响
有时,一个对象可能会有多个引用:
Point p1 = new Point(1, 2);
Point p2 = p1; // 两个引用指向同一个对象
p1 = null; // 只剩 p2 引用,对象不可回收
p2 = null; // 无引用,对象可回收
注意!
- 当
p1 = null时,对象不会被回收,因为p2还指向它; - 只有当 所有引用都断开 时,垃圾回收器才会考虑回收对象!
所以,即使一个对象失去了部分引用,也不代表它马上会被回收。
⚙️ 5. 垃圾回收器的工作机制
Java 的 GC 并不是随时都在运行,而是由 JVM 在合适的时机触发:
🌟 触发条件
- 内存不足时
- 系统空闲时
- 手动建议触发:
System.gc(); // 建议 JVM 执行 GC
⚠️ 注意!
System.gc() 只是建议,JVM 可以选择忽略。垃圾回收的时机完全由 JVM 自己决定!
🚀 标记-清除算法
垃圾回收的常见算法是 标记-清除算法:
- 标记阶段:从根对象 (比如栈帧变量、静态变量等) 出发,标记所有仍在使用的对象;
- 清除阶段:回收未被标记的对象,占用的内存被释放。
JVM 会使用 可达性分析算法 来判断对象是否存活,而不是简单地检查对象是不是 null。