对象的引用类型
- 强引用(Strong Reference):只要存在强引用,直到OOM也不会回收。如
Object obj = new Object。 - 软引用(Soft Reference):内存充足时不回收,内存不足(将要发生OOM)时才会进行回收。常用于实现内存敏感的高速缓存。
- 弱引用(Weak Reference):无论内存是否充足,只要发生GC就会被回收。
- 虚引用(Phantom Reference):目的是为了在对象被回收时收到一个系统通知。常用于配合引用队列跟踪堆外内存的释放(如
ByteBuffer的Cleaner)。
垃圾判定算法
引用计数法
为每一个对象添加一个引用计数器。每有一个对该对象的引用,计数器加一;引用失效时,计数器减一。计数为0时可以进行回收。
缺点:无法解决依赖循环问题,导致内存泄漏。因此Hotspot不使用该方法。
可达性分析算法
以一系列被称为GC Roots的根对象作为起点,从这些节点出发根据引用关系向下搜索,走过的路径称为“引用链”。如果一个对象与任何GC Roots都没有引用链相连,说明该对象不可达。
可以作为GC Roots的对象:
- 虚拟机栈中栈帧的局部变量表中引用的对象(如方法参数、局部变量)。
- 方法区中静态字段引用的对象(如
public static Object obj = new Object();)。 - 方法区中常量引用的对象(如
public static final修饰的引用)。 - 本地方法栈中JNI引用(Native方法)的对象。
- Java虚拟机内部的引用:
- 基本类型的Class对象。
- 常驻的异常对象(如
NullPointerException、OutOfMemoryError等)。 - 系统类加载器。
- 所有被同步锁(synchronized)持有的对象。
二次标记和finalize()自救
在判断对象不可达后,如果对象重写了finalize()且未被执行过,对象会被放入F-Queue队列,等待一个低优先级的Finalizer线程执行。如果对象在finalize()中将自己的引用(this)赋值给某个引用变量,在第二次小规模标记中就能避免被回收。
Warning
任何对象的finalize()只会被虚拟机调用一次。因其执行时间不可控,已经被废弃,不能用于资源释放。
垃圾回收算法
分代收集假说
- 弱分代假说:绝大多数对象都是朝生夕灭的(即大多数对象创建后很快就不再使用)。
- 强分代假说:经过越多次垃圾回收的对象,就越难死亡。
- 跨代引用假说:跨代引用相对于同代引用来说只占极少数。
三种算法
| 标记-清除算法(Mark-Sweep) | 标记-复制 (Copying) | 标记-整理 (Mark-Compact) | |
|---|---|---|---|
| 原理 | 标记存活对象、回收未标记对象 | 将内存分成两块, 回收时将存活对象复制到另一块, 清空原区域 | 标记存活对象, 将标记的对象全部移至一边, 清理边界外的区域 |
| 优点 | 实现简单、不需要移动对象 | 没有内存碎片、按序分配、效率高 | 没有内存碎片、不会浪费内存空间 |
| 缺点 | 会产生大量内存碎片 | 存在内存浪费、存活率高时复制开销大 | 移动对象需要更新所有引用, STW(Stop The World)时间较长 |
| 适用区域 | CMS的老年代 | 新生代(Appel 式 8:1:1) | Serial Old、Parallel Old的老年代 |
垃圾回收的范围
• Minor GC(Young GC):只回收新生代(Eden + S0/S1)。
• Major GC(Old GC):只回收老年代(目前只有 CMS 收集器有单独的老年代收集行为)。
• Mixed GC:回收整个新生代以及部分老年代(G1 收集器特有)。
• Full GC:回收整个 Java 堆和方法区。
回收过程
设最初使用的Survivor区为S0。
- 新对象不断在Eden分配。
- 当Eden满时,触发
Minor GC:- 扫描Eden和S0。
- 标记存活对象。
- 将存活的对象复制到S1。
- 将存活对象的年龄+1(记录在对象头中)。
- 清空Eden和S0。
- 交换S0和S1的角色。
- 晋升机制:
- 长期存活的对象:年龄达到阈值(默认15)时进入老年代。
- 大对象直接晋升老年代:大小超过一定阈值的对象直接进入老年代,如很长的字符串或很大的byte数组。避免在Eden和Survivor之间进行大量内存复制。
- 内存分配担保:Minor GC存活的对象无法完全放入目标Survivor区时,通过内存分配担保借用老年代内存。
- 动态对象年龄判定:Survivor区中,如果存在一个最小年龄N,使得从1到N的对象大小总和超过了阈值(默认50%),则年龄大于等于N的对象晋升为老年代。
相关参数:
-XX:MaxTenuringThreshold:晋升老年代年龄阈值,默认为15(CMS、G1),最大值为15。-XX:PretenureSizeThreshold:大对象直接进入老年代的阈值。适用于Serial和ParNew。