GC

287 阅读4分钟

1. 对象在什么时候变为垃圾

判断标准:没有被其他对象引用

1.1 引用计数法

  • 优点:执行效率高,程序执行受影响小
  • 缺点:循环引用

1.2 可达性分析算法

通过判断对象的引用链是否可达来决定对象是否可被回收

结构图

可达性分析算法

可以作为 GC Root 的对象

  • 虚拟机栈中引用的对象(栈帧中的本地变量表)
  • 方法区中的常量引用对象
  • 方法区中的类静态属性引用的对象
  • 本地方法栈中 JNI (Native 方法)的引用对象
  • 活跃线程的引用对象

2. GC 算法

2.1 标记-清除算法 (Mark and Sweep)

  • 标记:从 GC Root (可达性分析算法) 进行扫描,对存活的对象进行标记
  • 清除:对堆内存进行从头到尾的遍历,对标记对象进行清理

标记清除算法
如图所示,标记清除会导致内存碎片化

2.2 复制算法 (Copying)

该算法把内存分为两个区域:对象面和空闲面

  • 对象在对象面上创建
  • 存活的对象将会从对象面中复制到空闲面
  • 最后将对象面的所有对象的内存释放 ps:该算法适用于对象存活率低的场景 (一般用于回收年轻代)

  • 优点
    • 解决了碎片化问题
    • 顺序分配内存,简单高效
    • 适用与对象存活率低的场景
  • 缺点
    • 需要两倍的内存空间
    • 当对象存活率高的时候,需要复制的对象较多

2.3 标记-整理算法 (Compacting)

  • 标记:与标记清除相同
  • 整理:移动所有存活对象,且按照内存地址次序依次排列,然后将末端地址以后的内存全部回收

标记-整理算法

  • 优点
    • 避免了内存的不连续行
    • 不用设置两块互换内存
    • 适用于存活率高的场景

2.4 分代收集算法

按照对象的生命周期划分出不同的区域以采用不同的垃圾回收算法
目的:提高 JVM 垃圾回收的效率

版本差异

JDK 6 与 JDK 7

JDK 8 及以后

2.4.1 Young Generation

目的:尽可能块的收集掉那些生命周期短的对象

  • Eden 区
  • 两个 Survivor 区

当 Eden 空间不足时,会触发 Minor GC

2.4.2 Old Generation

目的:存放生命周期较长的对象

Full GC
触发 Full GC 的条件
  • 老年代空间不足
  • JDK 7 及之前的版本,永久代空间不足
  • CMS GC 时,出现 promotion failed 或者 concurrent mode failure
  • Minor GC 晋升到老年代的平均大小大于老年代的剩余空间
  • 调用 System.gc() (还是由虚拟机决定是否 GC)

3. GC 相关面试题

3.1 Object 的 finalize() 方法是否与 C++ 中的析构函数类似

  • C++ 中的析构函数的调用时机是确定的,而 Java 中的是不确定的
  • Java 中将未被引用的对象放在 F-Queue 队列
  • Java 中的 finalize 方法执行随时可能会被终止
  • 对象最后一次"重生"机会

3.2 Java 中强引用、软引用、弱引用和虚引用有什么用

3.2.1 强引用 (Strong Reference)

  • 最普遍的引用:Object obj = new Object();
强引用在 GC 中的特性
  • 在 GC 中,GC 宁可抛出 OutOfMemoryError 终止程序,也不会回收具有强引用的对象
  • 如果需要使用 GC 来回收强引用,将对象置为 null 来弱化引用,从而被 GC 回收

3.2.2 软引用 (Soft Reference)

  • 表示一个对象处于有用但非必须的状态
  • 只有在内存空间不足时 GC 才会回收该对象使用的内存空间
  • 可以用来实现内存敏感的高速缓存
// 软引用的创建方法
SoftReference<String> stringSoftReference = new SoftReference<String>("abc");

3.2.3 弱引用 (Weak reference)

  • 非必须的对象,比软引用更弱一些
  • GC 的时候会被回收,但是被回收的概率也并不大,毕竟 GC 线程的优先级很低
  • 适用于引用偶尔被使用且不影响垃圾收集的对象
// 弱引用的创建方法
WeakReference<String> stringWeakReference = new WeakReference<String>("abc");

3.2.4 虚引用 (Phantom Reference)

  • 不会决定内存的生命周期
  • 任何时候都可能被 GC 回收
  • 主要用来跟踪对象被垃圾收集器回收的活动,起哨兵的作用
  • 虚引用必须和引用队列 ReferenceQueue 联合使用
// 虚引用的用法
ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();
PhantomReference phantomReference = new PhantomReference("abc", referenceQueue);

3.2.5 层次结构

3.2.6 引用队列 (Reference Queue)

  • 虽然名字中有队列,但无实际存储结构,存储逻辑依赖于内部节点之间的关系来表达
  • 存储关联且被 GC 的软引用,弱引用和虚引用
  • 虚、