Java中的垃圾回收机制(Garbage Collection, GC)
Java 的垃圾回收机制是由 JVM(Java Virtual Machine,Java 虚拟机)负责自动管理内存的功能。它的核心是通过分析程序中的对象引用关系,自动释放不再使用的对象占用的内存空间,以便新对象使用,同时减少内存泄漏和内存溢出风险。
一、Java垃圾回收的目标
- 释放无用对象所占用的内存:自动回收不再使用的对象。
- 优化内存利用效率:通过整理和压缩内存,减少碎片化。
- 降低开发者的内存管理成本:开发者无需手动释放内存,降低错误概率。
二、Java内存结构
Java 的垃圾回收机制主要针对 堆内存(Heap Memory)。在 JVM 中,内存分为以下区域:
-
方法区(Method Area):
- 存储类信息、常量、静态变量、运行时常量池等。
- 在 JDK 8 之前使用 永久代(PermGen),之后替换为 元空间(Metaspace)。
- 注意:方法区的内存回收主要针对运行时常量池和无用类信息。
-
堆(Heap):
-
存储对象实例,是垃圾回收的主要区域。
-
按生命周期分为
新生代(Young Generation)
和
老年代(Old Generation)
。
- 新生代:存放生命周期短的对象。
- 老年代:存放生命周期长的对象。
-
-
栈(Stack):
- 存储局部变量和方法调用信息,不由垃圾回收器管理。
-
程序计数器和本地方法栈:
- 与垃圾回收无关。
三、Java垃圾回收的流程
1. 可达性分析(Reachability Analysis)
Java 的垃圾回收机制使用 可达性分析法 来判断对象是否可被回收。
- 从一组特殊的对象(GC Roots)出发,遍历所有可以直接或间接到达的对象。
- 如果某个对象无法通过 GC Roots 到达,则被认为是不可达对象,可被回收。
GC Roots 包括:
- 栈中局部变量引用的对象。
- 静态字段引用的对象。
- 常量池引用的对象。
- JNI(Java Native Interface)引用的对象。
2. 垃圾回收的基本算法
Java 垃圾回收器使用以下算法进行内存回收:
(1)标记-清除算法(Mark-Sweep)
- 标记阶段:从 GC Roots 开始标记所有可达的对象。
- 清除阶段:回收未被标记的对象。
优点:简单、实现方便。
缺点:回收后内存会产生碎片,影响后续内存分配效率。
(2)复制算法(Copying)
- 将内存分为两部分,每次只使用其中一部分。
- 活跃对象从当前区域复制到另一区域,剩下的整个区域被清空。
优点:没有碎片化问题,分配内存高效。
缺点:需要额外的内存空间(通常是总内存的 2 倍)。
应用:新生代使用此算法。
(3)标记-整理算法(Mark-Compact)
- 标记所有存活对象。
- 将存活对象移动到内存的一端,清理无用对象并释放剩余内存。
优点:解决了内存碎片化问题。
缺点:移动对象需要额外的性能开销。
应用:老年代使用此算法。
(4)分代收集算法(Generational Collection)
-
将内存划分为
新生代
和
老年代
,根据对象的生命周期采用不同算法:
- 新生代:对象存活时间短,使用 复制算法。
- 老年代:对象存活时间长,使用 标记-整理算法。
四、Java垃圾回收器(Garbage Collectors)
JVM 提供了多种垃圾回收器,以适应不同的应用场景。
1. Serial GC
- 单线程垃圾回收器,适合单线程程序。
- 新生代使用 复制算法,老年代使用 标记-整理算法。
特点:简单高效,但在多核 CPU 下性能较低。
适用场景:桌面应用或内存较小的单线程环境。
2. Parallel GC
- 多线程垃圾回收器,适合高吞吐量场景。
- 新生代和老年代分别使用 复制算法 和 标记-整理算法。
特点:通过多线程并行执行回收操作,提高效率。
适用场景:注重吞吐量的应用(如后台批处理)。
3. CMS GC(Concurrent Mark-Sweep)
- 低延迟垃圾回收器,专注于减少垃圾回收的停顿时间。
- 使用 标记-清除算法,并在标记阶段实现与应用线程的并发执行。
特点:
- 减少停顿时间,适合响应时间敏感的应用。
- 会产生内存碎片。
适用场景:需要低延迟的服务(如 Web 应用)。
4. G1 GC(Garbage-First Garbage Collector)
- 现代垃圾回收器,按分区回收内存,结合了分代收集的优点。
- 将堆划分为多个区域(Region),按优先级回收垃圾最多的区域。
特点:
- 高效管理内存,适合大内存场景。
- 可配置最大停顿时间。
适用场景:大规模、低延迟应用(如分布式系统)。
五、垃圾回收的触发条件
- 新生代满:当 Eden 区满时,会触发 Minor GC。
- 老年代满:当老年代满时,会触发 Major GC 或 Full GC。
- 显式调用:调用
System.gc()
或Runtime.getRuntime().gc()
,提示 JVM 执行垃圾回收(可能被忽略)。 - 元空间不足:方法区或元空间内存不足时,触发清理。
六、垃圾回收的调优
-
设置垃圾回收器:
-XX:+UseSerialGC -XX:+UseParallelGC -XX:+UseConcMarkSweepGC -XX:+UseG1GC
-
配置堆内存大小:
-Xms512m # 最小堆内存 -Xmx1024m # 最大堆内存
-
调整新生代与老年代比例:
-XX:NewRatio=2 # 新生代与老年代比例为 1:2
-
设置目标停顿时间(G1 GC):
-XX:MaxGCPauseMillis=200
七、总结
Java 的垃圾回收机制通过 自动化内存管理 减少开发者负担,但选择合适的垃圾回收器和合理的内存配置是关键。常见策略包括:
- 吞吐量优先:使用 Parallel GC。
- 延迟优先:使用 CMS 或 G1 GC。
- 小型单线程应用:使用 Serial GC。