### Java 垃圾回收机制概述
Java 垃圾回收(Garbage Collection,简称 GC)是 Java 语言的一个重要特性,它负责自动回收不再使用的对象所占用的内存空间,从而减轻了程序员手动管理内存的负担。
### 基本原理 1. 可达性分析(Reachability Analysis)
Java 虚拟机(JVM)通过可达性分析来确定哪些对象是“存活”的,哪些是“垃圾”。具体来说,JVM 会从一系列被称为“GC Roots”的对象开始,通过引用关系遍历对象图。如果一个对象可以从 GC Roots 到达,那么这个对象就是“存活”的;反之,如果一个对象无法从 GC Roots 到达,那么这个对象就被认为是“垃圾”,可以被回收。
常见的 GC Roots 包括:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象。 2. 垃圾标记
在可达性分析完成后,JVM 会标记出所有不可达的对象。这些对象将被视为垃圾,可以被回收。
3. 垃圾回收
JVM 会根据不同的垃圾回收算法来回收标记为垃圾的对象。常见的垃圾回收算法包括:
- 标记 - 清除算法(Mark-Sweep) :先标记出所有需要回收的对象,然后在标记完成后统一回收所有被标记的对象。这种算法的缺点是会产生大量的内存碎片。
- 标记 - 整理算法(Mark-Compact) :先标记出所有需要回收的对象,然后将所有存活的对象向一端移动,最后清理掉端边界以外的内存。这种算法可以避免内存碎片的问题,但需要移动对象,效率相对较低。
- 复制算法(Copying) :将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这种算法的优点是实现简单,运行高效,但会浪费一半的内存空间。
- 分代收集算法(Generational Collection) :根据对象存活周期的不同将内存划分为几块,一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。例如,在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记 - 清理”或者“标记 - 整理”算法来进行回收。
### 垃圾回收的触发条件
- 新生代空间不足 :当新生代的 Eden 区或者 Survivor 区空间不足时,会触发 Minor GC(新生代垃圾回收)。
- 老年代空间不足 :当老年代空间不足时,会触发 Full GC(全量垃圾回收)。
- 调用 System.gc() 方法 :虽然调用 System.gc() 方法会建议 JVM 进行垃圾回收,但这只是一个建议,JVM 并不一定会立即执行垃圾回收。
### 总结
Java 垃圾回收机制通过可达性分析来确定哪些对象是垃圾,然后根据不同的垃圾回收算法来回收这些垃圾对象。这种自动内存管理机制大大提高了开发效率,减少了内存泄漏和悬空指针等问题的发生。
1. 新生代(Young Generation)
- 存放新创建的对象
- 采用复制算法进行垃圾回收(Minor GC)
- 分为1个Eden区和2个Survivor区
- GC频率高但速度快
- 对象存活时间短(默认15次GC后存活则晋升老年代)
2. 老年代(Old Generation)
- 存放长期存活的对象
- 采用标记-整理/清除算法(Major GC/Full GC)
- GC频率低但耗时长
- 空间通常是新生代的2-3倍
3. Eden区
- 新生代的初始分配区(对象出生地)
- 占新生代80%空间
- 当Eden区满时触发Minor GC
4. Survivor区
- 包含From和To两个区域
- 存放Minor GC后存活的对象
- 通过复制算法在Survivor区间转移
- 避免内存碎片化
Eden区 (伊甸园区)
- 中文直译:"伊甸园"(取自圣经中的生命起源之地)
- 作用:新对象出生的地方,所有新创建的对象首先在Eden区分配内存
Survivor区 (幸存者区)
- 中文直译:"幸存者"
- 作用:存放Minor GC后存活的对象,分为两个子区域:
- From区 :当前存活的Survivor空间(上一次GC的幸存者)
- To区 :用于存放本次GC新晋升的幸存者
// 以示例中的200MB对象为例:
1. 首次GC:Eden区存活对象 -> 复制到From区
2. 下次GC:Eden + From区存活对象 -> 复制到To区(年龄+1)
3. 每次GC后From和To交换角色
区域转换机制 (动态交换)
- 年龄计数器:对象每经历一次Minor GC年龄加1
- 晋升规则:
- 默认年龄15晋升老年代(可通过 -XX:MaxTenuringThreshold 调整)
- Survivor空间不足时提前晋升
- 年龄跟踪解决"对象该在哪代",标记-整理解决"该代如何回收"的问题。
Java垃圾回收(GC)是自动内存管理机制,主要原理:1) 分代收集算法将堆内存分为新生代和老年代 2) 可达性分析算法判断对象是否存活 3) 使用标记-清除、复制、标记-整理算法回收内存 4) 通过Stop-The-World暂停应用线程执行回收。GC会定期扫描堆内存,自动回收不可达对象占用的空间,开发者无需手动释放内存。
Java垃圾回收的分代机制:
1. 新生代(Young Generation):存放新建对象,采用复制算法快速回收
2. 老年代(Old Generation):存放长期存活对象,使用标记-整理算法
可达性分析算法:
通过GC Roots(如静态变量/活动线程等)作为起点,遍历对象引用链。若对象与GC Roots之间无引用路径,则判定为可回收对象。这是判断内存是否可回收的核心机制。
复制算法用于新生代垃圾回收,将存活对象复制到Survivor区(类似搬家整理)。标记-整理算法用于老年代:1) 标记存活对象 2) 将对象向内存一端移动整理 3) 清除边界外内存。两种算法都通过对象移动避免内存碎片,但整理算法更节省空间。