【JVM】垃圾回收
垃圾确定
引用计数法
- 概念:对于某个对象而言,只要应用程序中持有该对象的引用,就说明该对象不是垃圾,如果一个对象没有任何指针对其引用,它就是垃圾。
- 缺点:如果AB相互持有引用,导致永远不能被回收。
可达性分析
- 概念:通过
GC Root
对象,开始向下寻找,看某个对象是否可达。 - 能作为
GC Root
的对象:类加载器、Thread、虚拟机栈的本地变量表、static成员、常量引用、本地方法栈的变量等。
垃圾收集算法
标记-清除(Mark-Sweep)
标记
- 找出内存中需要回收的对象,并且把它们标记出来,
- 堆中所有的对象都会被扫描一遍,从而才能确定需要回收的对象,比较耗时
清除
- 清除掉被标记需要回收的对象,释放出对应的内存空间
缺点
- 标记清除之后会产生大量不连续的内存碎片,导致因无法找到足够的连续内存分配大对象从而触发GC。
- 标记和清除两个过程都比较耗时,效率不高。
复制(Copying)
流程
- 将内存划分为两块相等的区域,每次只使用其中一块,如下图所示:
- 当其中一块内存使用完了,就将还存活的对象复制到另外一块上面,然后把已经使用过的内存空间一次清除掉。
缺点
- 空间利用率降低。
标记-整理(Mark-Compact)
流程
- 标记过程仍然与"标记-清除"算法一样,但是后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
分代收集算法
- Young区:复制算法(对象在被分配之后,可能生命周期比较短,Young区复制效率比较高)
- Old区:标记清除或标记整理(Old区对象存活时间比较长,复制来复制去没必要,不如做个标记再清理)
垃圾收集器
吞吐量和停顿时间
- 吞吐量:运行用户代码时间 / 运行用户代码时间 + 垃圾收集时间。
- 停顿时间:垃圾收集器进行垃圾回收终端应用执行响应的时间。
- 停顿时间越短就越适合需要和用户交互的程序,良好的响应速度能提升用户体验。
- 高吞吐量则可以高效地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任
务。 - 这两个指标也是评价垃圾回收器好处的标准,其实调优也就是在观察者两个变量。
Serial收集器
Serial Old收集器
ParNew收集器
Parallel Old收集器
Parallel Scavenge收集器
CMS收集器
目标
- 获取最短回收停顿时间
算法
- 标记-清除
特性
- 老年代的垃圾收集器
- 并发收集、低停顿
- 非常符合互联网站或者B/S系统的服务端上,重视服务的响应速度,希望系统停顿时间最短的应用。
- 内存回收过程与用户线程一起并发地执行
- 对CPU资源非常敏感
- 产生大量空间碎片、并发阶段会降低吞吐量
流程
- 初始标记(initial mark):标记GC Roots能关联到的对象,速度很快,但是需要暂停用户线程(Stop The World)
- 并发标记(concurrent mark):进行GC Roots Tracing,可以和用户线程并发执行
- 重新标记(remark):修改并发标记因用户程序变动的内容,需要STW
- 并发清除(concurrent sweep):可以和用户线程并发执行
G1收集器
算法
- 标记-整理
特性
- 并行与并发:充分利用多CPU、多核环境下的硬件优势。
- 分代收集:不需要其他收集器配合就能独立管理整个GC堆
- 空间整合:不会导致空间碎片
- 可预测的停顿:能让使用者明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒
- 整个Java堆划分为多个大小相等的独立区域(Region)
- JDK 7开始使用,JDK 8非常成熟,JDK 9默认的垃圾收集器,适用于新老生代
流程
- 初始标记(Initial Marking):标记一下GC Roots能够关联的对象,并且修改TAMS的值,耗时很短,需要STW
- 并发标记(Concurrent Marking):从GC Roots进行可达性分析,找出存活的对象,耗时较长,可以与用户线程并发执行
- 最终标记(Final Marking):修正在并发标记阶段因为用户程序的并发执行导致变动的数据,需要STW
- 筛选回收(Live Data Counting and Evacuation):对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间制定回收计划
垃圾收集器比较
如何选择合适的垃圾收集器
官网参考:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html#sthref28
- 优先调整堆的大小让服务器自己来选择
- 如果内存小于100M,使用串行收集器
- 如果是单核,并且没有停顿时间要求,使用串行或JVM自己选
- 如果允许停顿时间超过1秒,选择并行或JVM自己选
- 如果响应时间最重要,并且不能超过1秒,使用并发收集器
如何开启需要的垃圾收集器
-XX:+UseConcMarkSweepGC
:使用CMS垃圾收集器-XX:+UseG1GC
:使用G1垃圾收集器
评论区