Java 的垃圾回收(Garbage Collection,GC)机制是自动管理内存的重要组成部分,旨在释放不再使用的对象所占用的内存。以下将详细介绍 Java 的堆结构、分配回收规则、死亡对象的判断方法以及 GC 的工作原理。
1. 堆的结构
Java 堆是 JVM 用于存储对象的内存区域,通常被称为 GC 堆。根据不同的 JDK 版本,堆的结构有所不同:
- JDK 1.7:新生区(Young Generation)、老年区(Old Generation)、永久区(Permanent Generation)
- JDK 1.8:新生区(Young Generation)、老年区(Old Generation)、元空间(Metaspace)
2. 分配回收规则
2.1 小对象的分配
- 直接放入新生区:如果新生区未满,直接将对象放入新生区。
- 小 GC(Minor GC):
- 当新生区满时,执行小 GC。
- 对象可以直接放入老年区进行内存空间担保。
- 如果老年区仍然满,则执行老年区的专业 GC。
2.2 大对象的分配
- 直接进入老年区:如字符串、数组等大对象会直接被分配到老年区。
2.3 长期存活的对象
- 对象在每次小 GC 后年龄加一,默认达到 15 岁时进入老年区。
2.4 内存空间担保
- 确保在 Minor GC 之前,老年代有足够空间容纳新生代所有对象。
3. GC 总结
3.1 垃圾收集类型
-
部分收集(Partial GC):
- 新生代收集(Minor GC):只对新生代进行垃圾收集。
- 老年代收集(Major GC):只对老年代进行垃圾收集。
-
混合收集(Mixed GC):
- 对整个新生代和部分老年代进行垃圾收集。
-
整堆收集(Full GC):
- 收集整个 Java 堆和方法区。
4. 死亡对象的判断方法
4.1 引用计数器
- 每当有地方引用对象,计数器加 1;引用失效则减 1。
- 问题:循环引用导致计数器永远不为 0,GC 无法回收。
4.2 可达性分析
- 通过一系列称为 “GC Roots” 的对象作为起点,向下搜索引用链。
- 如果一个对象到 GC Roots 没有任何引用链相连,则证明该对象不可用,需要被回收。
GC Roots 示例
- 虚拟机栈中的局部变量表引用的对象
- 本地方法栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 被同步锁持有的对象
- JNI(Java Native Interface)引用的对象
5. 四种引用类型
- 强引用:最常见的引用类型,GC 不会回收。
- 软引用:用于描述一些还有用但非必需的对象,内存不足时会被回收。
- 弱引用:描述非必需对象,GC 会在下一次回收时回收。
- 虚引用:用于跟踪对象的回收,无法通过虚引用获取对象。
6. GC 如何判断对象死亡
- 对象没有任何实例。
- 没有任何引用指向该对象。
- 该类的类加载被回收。
7. 新生代与老年代的分区原因
- 不同的回收算法:
- 新生代:对象死亡率高,使用“标记-复制”算法。
- 老年代:对象存活率高,使用“标记-清除”或“标记-整理”算法。
结论
理解 Java 的垃圾回收机制对于优化内存管理、提高应用性能至关重要。通过合理配置 GC 策略和理解对象的生命周期,开发者可以更有效地控制内存使用,避免内存泄漏和性能问题。