JVM垃圾回收

GC回收位置

程序计数器虚拟机栈本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收。垃圾回收主要是针对 Java堆方法区进行。

判断对象可回收算法

引用计数算法(Reference Counting)

给对象添加一个引用计数器,记录被引用次数;当计数器为0时立即回收。会存在循环引用的问题。

PS:高级的引用计数实现会引入“弱引用”的概念来打破某些已知的循环引用。

可达性分析算法(GC Roots Tracing)

根节点(GC Roots) 开始向下搜索,当根节点对象到某个对象不可达时,则此对象被判定为是可回收的对象,随后进行批量回收。

PS:对象可通过finalize()方法逃脱回收,且只会执行一次。

垃圾收集算法

标记-清除

标记需要清除的对象,标记完成后统一清除。会产生内存碎片。

标记-整理

标记需要存活的对象,标记完成后进行向一端整理(保持内存连续),然后直接清理掉端边界以外的内存。

PS:标记:1.标记存活的对象,然后清除未标记的对象;2.标记要回收的对象然后清除。

复制回收

将可用的内存按容量划分为大小相同的两块,每次只是用其中的一块。当这块内存用完了,就将还存活着的对象复制到另一块上面,然后把已经使用过的内存空间一次清理掉。jvm年轻代使用复制算法分成 eden:s0:s1 = 8:1:1,将eden+s0 存活的对象复制到 s1,然后清理掉eden+s0。

分代收集

现在的商业虚拟机一般采用分代收集算法,它根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。

一般将堆分为新生代和老年代:

  • 新生代使用:复制回收
  • 老年代使用:标记-清除 或者 标记-整理

垃圾收集器

image

Serial 收集器

串行收集器:单线程工作的收集器,工作时会"Stop the world"

  • 新生代采取复制算法(单线程),暂停所有用户线程。
  • 老年代采取标记-整理算法(单线程),暂停所有用户线程。

ParNew 收集器

Serial 收集器的多线程版本

  • 新生代采取复制算法(多线程并行收集)暂停所有用户线程。
  • 老年代采取标记-整理算法(单线程)暂停所有用户线程。

默认开启的线程数量与 CPU 数量相同,可以使用 -XX:ParallelGCThreads 参数来设置线程数。

Parallel Scavenge 收集器

Parallel Scavenge 收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器

  • 自适应调节策略
  • 吞吐量优先
  • 最大垃圾收集停顿时间: -XX:MaxGCPauseMills
  • 吞吐量大小:-XX:GCTimeRatio

Serial Old 收集器

Serial Old是Serial收集器的老年代版本

  • 新生代采取复制算法(单线程),暂停所有用户线程。
  • 老年代采取标记-整理算法(单线程),暂停所有用户线程。

Parallel Old 收集器

Parallel Old是Parallel Scavenge收集器的老年代版本

  • 新生代和老年代都采用多线程并发收集。

CMS 收集器(Concurrent Mark Sweep)

并发收集、低停顿,基于 标记-清除 算法实现的收集器
过程

  • 初始标记:仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿。
  • 并发标记:从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。
  • 重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿。
  • 并发清除:清理删除掉标记阶段判断的已经死亡的对象,不需要停顿。

缺点:吞吐量低,浮动垃圾,内存碎片

Garbage First 收集器(G1)

G1(Garbage-First),它是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。
G1将新生代,老年代的物理空间划分取消了
G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。

过程

  • 初始标记
  • 并发标记
  • 最终标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的 Remembered Set Logs 里面,最终标记阶段需要把 Remembered Set Logs 的数据合并到 Remembered Set 中。这阶段需要停顿线程,但是可并行执行。
  • 筛选回收:首先对各个 Region 中的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分 Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率。

特点:

  • 空间整合:整体来看是基于“标记 - 整理”算法实现的收集器,从局部(两个 Region 之间)上来看是基于“复制”算法实现的,这意味着运行期间不会产生内存空间碎片。
  • 可预测的停顿:能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在 GC 上的时间不得超过 N 毫秒。

比较

收集器 单线程/并行 串行/并发 新生代/老年代 收集算法 目标 适用场景
Serial 单线程 串行 新生代 复制 响应速度优先 单 CPU 环境下的 Client 模式
ParNew 并行 串行 新生代 复制算法 响应速度优先 多 CPU 环境时在 Server 模式下与 CMS 配合
Parallel Scavenge 并行 串行 新生代 复制算法 吞吐量优先 在后台运算而不需要太多交互的任务
Serial Old 单线程 串行 老年代 标记-整理 响应速度优先 单 CPU 环境下的 Client 模式、CMS 的后备预案
Parallel Old 并行 串行 老年代 标记-整理 吞吐量优先 在后台运算而不需要太多交互的任务
CMS 并行 并发 老年代 标记-清除 响应速度优先 集中在互联网站或 B/S 系统服务端上的 Java 应用
G1 并行 并发 新生代 + 老年代 标记-整理 + 复制算法 响应速度优先 面向服务端应用,将来替换 CMS

使用

新生代(别名) 老年代 JVM 参数
Serial (DefNew) Serial Old(PSOldGen) -XX:+UseSerialGC
Parallel Scavenge (PSYoungGen) Serial Old(PSOldGen) -XX:+UseParallelGC
Parallel Scavenge (PSYoungGen) Parallel Old (ParOldGen) -XX:+UseParallelOldGC
ParNew (ParNew) Serial Old(PSOldGen) -XX:-UseParNewGC
ParNew (ParNew) CMS+Serial Old(PSOldGen) -XX:+UseConcMarkSweepGC
G1 G1 -XX:+UseG1GC

查看jvm

查看jvm配置
java -XX:+PrintCommandLineFlags -version
查看gc详细信息
java -XX:+PrintGCDetails -version

posted @ 2021-04-21 16:10  Masze  阅读(59)  评论(0)    收藏  举报