GC

内存分配

对象优先在 eden 区分配
大对象直接进入老年代
长期存活的对象将进入老年代
动态对象年龄判定
主要进行 gc 的区域
空间分配担保

对象死亡分析

引用计数

很难解决对象之间相互循环引用的问题

可达性分析

以 GC 根为起点向下搜索
哪些对象可以作为 GC Roots 呢?
  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 本地方法栈(Native 方法)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 所有被同步锁持有的对象

引用类型

强引用

垃圾回收器绝不会回收,内存空间不足时虚拟机抛出 OutOfMemoryError

软引用

如果内存空间足够,垃圾回收器就不会回收它。可以用来实现缓存

弱引用

在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

虚引用

任何时候都可能被垃圾回收

废弃常量

没有任何引用的常量

无用的类

  • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
  • 该类对应的 Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
  • 加载该类的 ClassLoader 已经被回收。

垃圾收集算法

标记-清除算法

出现内存碎片问题

标记-复制算法

标记-整理算法

分代收集算法

垃圾收集器

Serial 收集器

新生代采用标记-复制算法,老年代采用标记-整理算法。

ParNew 收集器

ParNew 收集器其实就是 Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。
新生代采用标记-复制算法,老年代采用标记-整理算法。

Parallel Scavenge 收集器

Parallel Scavenge 收集器关注点是吞吐量(高效率的利用 CPU)。CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)
新生代采用标记-复制算法,老年代采用标记-整理算法。
这是 JDK1.8 默认收集器

Serial Old 收集器

Serial 收集器的老年代版本

Parallel Old 收集器

Parallel Scavenge 收集器的老年代版本。

CMS 收集器(Concurrent Mark Sweep)

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用。
整个过程分为四个步骤:
  • 初始标记: 暂停所有的其他线程,并记录下直接与 root 相连的对象,速度很快 ;
  • 并发标记: 同时开启 GC 和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为用户线程可能会不断的更新引用域,所以 GC 线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方。
  • 重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短
  • 并发清除: 开启用户线程,同时 GC 线程开始对未标记的区域做清扫。
“标记-清除”算法会导致收集结束时会有大量空间碎片产生

G1 收集器(Garbage-First)

G1 收集器的运作大致分为以下几个步骤:
  • 初始标记
  • 并发标记
  • 最终标记
  • 筛选回收

ZGC 收集器

与 CMS 中的 ParNew 和 G1 类似,ZGC 也采用标记-复制算法,不过 ZGC 对该算法做了重大改进。