Java 的垃圾回收机制(Garbage Collection, GC)是自动管理内存的一项重要功能,主要用于回收那些不再被使用的对象所占的内存空间。它通过自动化的内存管理,减少开发者手动释放内存的负担,避免了内存泄漏和悬挂指针等问题。
1. 垃圾回收机制的核心
Java 的垃圾回收机制通过追踪程序中哪些对象是“可达”的,哪些对象已经变得“不可达”来确定是否需要回收对象。不可达的对象会被认为是“垃圾”,然后垃圾回收器会回收这些对象的内存。
垃圾回收器的主要目标是:
- 识别那些不再被任何活跃部分引用的对象。
- 自动回收这些对象所占的内存空间。
2. 主要处理的对象
垃圾回收器处理的是在堆内存中分配的对象,也就是通过 new
关键字创建的实例。当这些对象不再被任何其他对象引用时,它们就会被标记为可以回收。
主要处理的对象包括:
- 不再被引用的对象:例如局部变量离开作用域后指向的对象。
- 被强引用链断开的对象:如果某个对象的引用链断裂,它无法通过任何活跃的变量访问。
- 长时间未被使用的对象:即使对象存在较长时间,如果没有活跃的引用,也会被认为是可以回收的。
3. 垃圾回收的分代机制和区域划分
Java 垃圾回收器将堆内存分为不同的区域,并采用分代收集算法(Generational Collection)。它根据对象的生命周期和存活时间的特点进行优化,堆内存主要分为以下两个部分:
1. 新生代(Young Generation)
新生代是存储刚刚创建的对象的区域,大部分新创建的对象存活时间较短。新生代又细分为三个区域:
- Eden 区:新对象最初分配在 Eden 区,当 Eden 区填满时,会触发一次 Minor GC。
- Survivor 区:Eden 区对象经过 Minor GC 存活下来的对象会被复制到 Survivor 区。Survivor 区分为两个部分:
From
和To
,每次 GC 后对象在两个区域间交替存放。
新生代的特点是对象的存活率低,因此采用了复制算法,在 GC 时将存活的对象从 Eden 区复制到 Survivor 区,然后清空 Eden 区和 From Survivor 区。
Minor GC:发生在新生代,当 Eden 区满时会触发。Minor GC 的速度相对较快,因为新生代对象的存活率低,回收大部分对象后释放空间。
2. 老年代(Old Generation)
老年代存放的是存活时间较长的对象。当对象在新生代经历多次 Minor GC 后仍然存活,它们会被晋升到老年代。
老年代的特点是对象存活时间长,存活率高,因此采用了标记-整理算法(Mark-Compact)。在这个区域进行垃圾回收时,由于对象的存活率较高,垃圾回收的效率较低,通常是老年代中的对象比较多时才会进行回收。
Major GC 或 Full GC:当老年代的内存不足时,触发 Full GC,这一过程会扫描整个堆内存(包括新生代和老年代),因此回收过程比 Minor GC 慢得多。
4. 垃圾回收处理的区域和对象
垃圾回收机制根据堆内存的区域划分和对象的生命周期,主要处理以下几类对象:
-
短命对象(Short-lived Objects):
- 新生代的对象,通常是刚刚创建但很快就不再需要的对象(如局部变量对象)。它们在 Eden 区被分配,在少量 GC 后,如果没有被引用,则会被回收。
- 这些对象通过 Minor GC 来回收。
-
长命对象(Long-lived Objects):
- 老年代的对象,通常是存活时间较长的对象(如静态变量、全局对象、长期存在的缓存对象)。它们在新生代经历多次 Minor GC 后被移动到老年代。
- 长期未使用的老年代对象会通过 Major GC 或 Full GC 进行回收。
5. GC Roots 和可达性分析
垃圾回收器通过 可达性分析算法 来判断哪些对象是可达的,哪些是不可达的。
-
GC Roots:作为根对象的集合,包括:
- 虚拟机栈中的局部变量表。
- 方法区中的类静态属性。
- 常量池中的引用。
- 本地方法栈中的 JNI 引用。
从 GC Roots 出发,通过一系列引用路径可以到达的对象是可达对象,这些对象不会被垃圾回收。反之,如果从 GC Roots 无法到达某个对象,则该对象被认为是不可达对象,会被标记为垃圾对象,准备回收。
6. 垃圾回收的种类
在 Java 中,垃圾回收主要分为以下两种类型:
-
Minor GC:
- 发生在新生代,回收新生代中的短命对象。
- 执行频率高,但耗时短。
-
Major GC 或 Full GC:
- 发生在老年代,回收老年代中的长命对象。
- 执行频率低,但耗时较长。Full GC 还会对整个堆进行回收,包括新生代和老年代。
7. 垃圾回收的触发条件
垃圾回收器会在以下几种情况下触发垃圾回收:
- 新生代的 Eden 区满时,会触发 Minor GC。
- 老年代空间不足时,会触发 Major GC 或 Full GC。
- 调用
System.gc()
,显式请求 JVM 执行垃圾回收(不一定立刻执行,JVM 会决定是否立即执行)。
总结
Java 的垃圾回收机制主要处理的是堆内存中的对象,尤其是那些不再被引用的对象。Java 采用分代收集的方式,堆内存分为新生代和老年代,根据对象的生命周期和存活率选择不同的垃圾收集算法。新生代主要通过 Minor GC 回收存活时间短的对象,老年代则通过 Major GC 或 Full GC 回收长存活的对象。通过自动的垃圾回收机制,Java 减少了开发者管理内存的负担,同时提高了内存使用的效率。