死亡对象判断方法
- 引用计数法
-
给对象中添加一个引用计数器:
- 每当有一个地方引用它,计数器就加 1;
- 当引用失效,计数器就减 1;
- 任何时候计数器为 0 的对象就是不可能再被使用的。
- 难以解决对象之间循环引用的问题
-
- 可达性分析算法
- 这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
- GCRoots节点包括:
- 虚拟机栈(栈帧中的局部变量表)中引用的对象
- 本地方法栈(Native 方法)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 所有被同步锁持有的对象
- JNI(Java Native Interface)引用的对象
垃圾回收算法
- 标记-清除
- 会有碎片
- 适合垃圾较少的情况
- 标记-复制
- 复制成本高
- 适合垃圾较多的情况
- 可用空间少一半
- 标记-整理
- 整理需要复制
- 适合垃圾较多的情况
- 分代回收
- 新生代和老年代各自有自己的回收算法(划分新生代和老年代就是为了针对不用对象特点使用不同的垃圾回收算法)
垃圾回收器
- Serial
- 单线程
- 算法
- 新生代:标记-复制
- 老年代:标记-整理
- 特点:垃圾回收的时候会STW(Stop The World)
- 优点:简单(除了简单没啥优点,后续的都是它的改进版)
- 缺点:STW时间长,不适合多线程
- ParNew
- Serial的多线程版本
- 算法
- 新生代:标记-复制
- 老年代:标记-整理
- 特点:多线程
- Parallel Scavenge
- 特点:注重吞吐量
- 算法
- 新生代:标记-复制
- 老年代:标记-整理
- Serial Old
- Serial的老年代版本
- 算法:标记-整理
- 单线程
- Parallel Old
- Parallel Scavenge的老年代版本
- 算法:标记-整理
- 多线程
- CMS
- 第一个真正意义上的并发收集器
- 特点:注重用户体验,以追求最短STW时间为优先
- 算法:标记-清除算法(CMS就是Concurrent Mark Sweep)
- 清除步骤
- 初始标记:记录和root直接相连的对象,很快
- 并发标记:GC线程和用户线程同时进行,记录所有可达对象
- 重新标记:第二步中可能有原先标记为可回收的对象又变为不可回收了,找出来,时间长一点
- 并发清除:清除垃圾
- 缺点
- 无法处理浮动垃圾(指原来不是垃圾的对象在回收过程中又成了垃圾,没法检测到)
- 对CPU资源敏感
- 使用的算法不太行,有碎片
- G1
- 适合在服务器(多CPU和大内存)上使用
- 算法
- 总体上是标记-整理,局部是标记-复制
- 内部维护了一个优先列表,每次回收垃圾都会从队列中取出当前STW时间内最值得回收的对象
- 特点
- 可预测STW时间
- 并行与并发:多CPU
- 分代收集
- 空间整合:与CMS的标记清除算法相比带来的好处
- 回收步骤
- 初始标记
- 并发标记
- 最终标记
- 筛选回收:对每个Region的回收成本进行排序,按照用户自定义的回收时间来制定回收计划
- ZGC
- 算法:标记-复制,不过ZGC对该算法做出了重大改进
- 可以将停顿时间控制在几毫秒以内,且暂停时间不受堆内存大小的影响,出现STW的情况更少,代价是牺牲了一些吞吐量