JVM真香系列:图解垃圾回收器

回复“000”获取大量电子书

不知不觉,JVM系列已经到回收算法的实现了。

本文主要内容

先普及三个概念:

并行收集:指多条垃圾收集线程并行工作,但此时用户线程仍处于等待状态。

并发收集:指用户线程与垃圾收集线程同时工作(不一定是并行的可能会交替执行)。用户程序在继续运行,而垃圾收集程序运行在另一个CPU上。

吞吐量:即CPU用于运行用户代码的时间与CPU总消耗时间的比值(吞吐量 = 运行用户代码时间 / ( 运行用户代码时间 + 垃圾收集时间 ))。

例如:虚拟机共运行100分钟,垃圾收集器花掉1分钟,那么吞吐量就是99%。

新生代

Serial

Serial收集器是最基本、发展历史最悠久的收集器,曾经(在JDK1.3.1之前)是虚拟机新生代收集的唯一选择。

它是一种单线程收集器,不仅仅意味着它只会使用一个CPU或者一条收集线程去完成垃圾收集工作,更重要的是其在进行垃圾收集的时候需要暂停其他线程。

优点:简单高效,拥有很高的单线程收集效率
缺点:收集过程需要暂停所有线程
算法:复制算法
应用:Client模式下的默认新生代收集器

收集过程:

ParNew

可以把这个收集器理解为Serial收集器的多线程版本。

优点:在多CPU时,比Serial效率高。
缺点:收集过程暂停所有应用程序线程,单CPU时比Serial效率差。
算法:复制算法
应用:运行在Server模式下的虚拟机中首选的新生代收集器

收集过程:

Parallel Scanvenge

Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器,看上去和ParNew一样,但是Parallel Scanvenge更关注系统的吞吐量 。

吞吐量 = 运行用户代码的时间 / (运行用户代码的时间 + 垃圾收集时间)

比如虚拟机总共运行了120秒,垃圾收集时间用了1秒,吞吐量=(120-1)/120=99.167%。

若吞吐量越大,意味着垃圾收集的时间越短,则用户代码可以充分利用CPU资源,尽快完成程序的运算任务。

可设置参数:

-XX:MaxGCPauseMillis控制最大的垃圾收集停顿时间,
-XX:GC Time Ratio直接设置吞吐量的大小。

老年代

CMS=Concurrent Mark Sweep

特点:最短回收停顿时间,

回收算法:标记-清除

回收步骤:

  1. 初始标记:标记GC Roots直接关联的对象,速度快

  2. 并发标记:GC Roots Tracing过程,耗时长,与用户进程并发工作

  3. 重新标记:修正并发标记期间用户进程运行而产生变化的标记,好事比初始标记长,但是远远小于并发标记

  4. 表发清除:清除标记的对象

缺点

对CPU资源非常敏感,CPU少于4个时,CMS岁用户程序的影响可能变得很大,由此虚拟机提供了“增量式并发收集器”;无法回收浮动垃圾;采用标记清除算法会产生内存碎片,不过可以通过参数开启内存碎片的合并整理。

收集过程:

serial old

Serial Old收集器是Serial收集器的老年代版本,也是一个单线程收集器,不同的是采用"标记-整理算法",运行过程和Serial收集器一样。

适用场景:

JDK1.5前与Parallel Scanvenge配合使用,作为CMS的后备预案。

收集过程:

Parallel old

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和"标记-整理算法"进行垃圾回收,吞吐量优先。

回收算法:标记-整理

适用场景

为了替代serial oldParallel Scanvenge配合使用。

收集过程:

G1=Garbage first

JDK 9 开始,JVM 的默认垃圾回收器就从 Parallel GC 调整为 G1,并且开始全面废除 CMS

限制或者减少 GC 停顿时间相比系统吞吐量而言更加重要,从 PGC 切换至低延迟的 G1 能够为大部分用户带来更好的体验。G1 的性能在 JDK 8 以及后续的 release 版本都得到了极大的优化,G1 是一个具备所有 GC 特性的垃圾回收器,因此将 G1 设置为 JVM 默认的 GC。

根据 JEP-291 中的说明,为了减轻 GC 代码的维护负担以及加速新功能开发,决定在 JDK 9 中废弃 CMS GC。

从 Java 9 开始,如果您使用

-XX:+UseConcMarkSweepGC

(激活 CMS GC 算法的参数)参数启动应用程序,则会在下面显示警告消息:

Java HotSpot(TM) 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.

如果你想知道当前应用对应的 JVM 版本,你可以使用以下命令进行查询:

G1将整个JVM堆划分成多个大小相等的独立区域regin,跟踪各个regin里面的垃圾堆积的价值大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收最大的regin,当然还保留有新生代和老年代的概念,但新生代和老年代不在是物理隔离了,他们都是一部分regin集合。

内存“化整为零”的思路:在GC根节点的枚举范围汇总加入remembered set 即可保证不对全堆扫面也不会遗漏。

回收步骤

  1. 初始标记:标记GC Roots直接关联的对象

  2. 并发标记:对堆中对象进行可达性分析,找出存活对象,耗时长,与用户进程并发工作

  3. 最终标记:修正并发标记期间用户进程继续运行而产生变化的标记

  4. 筛选回收:对各个regin的回收价值进行排序,然后根据期望的GC停顿时间制定回收计划

G1收集器优势

并行与并发G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-The-World停顿时间。部分收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让Java程序继续运行。

分代收集G1能够独自管理整个Java堆,并且采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。

空间整合G1运作期间不会产生空间碎片,收集后能提供规整的可用内存。

可预测的停顿G1除了追求低停顿外,还能建立可预测的停顿时间模型。能让使用者明确指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。

收集过程:

G1的回收过程主要分为 3 类:

(1)G1“年轻代”的垃圾回收,同样叫 Minor G1,这个过程和我们前面描述的类似,发生时机就是 Eden 区满的时候。

(2)老年代的垃圾收集,严格上来说其实不算是收集,它是一个“并发标记”的过程,顺便清理了一点点对象。

(3)真正的清理,发生在“混合模式”,它不止清理年轻代,还会将老年代的一部分区域进行清理。

ZGC

ZGC(Z Garbage Collector)是一款由Oracle公司研发的,以低延迟为首要目标的一款垃圾收集器。它是基于动态Region内存布局,(暂时)不设年龄分代,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法的收集器。

JDK 11新加入,还在实验阶段,主要特点是:回收TB级内存(最大4T),停顿时间不超过10ms。

优点:低停顿,高吞吐量,ZGC收集过程中额外耗费的内存小

缺点:浮动垃圾

目前使用的非常少,真正普及还是需要写时间的。

垃圾收集器搭配关系

新生代收集器:Serial、ParNewParallel Scavenge

老年代收集器CMS、Serial Old、Parallel Old

整堆收集器G1ZGC(因为不涉年代不在图中)

如何选择垃圾收集器?

前面我们已经介绍完八种垃圾收集器。那么我在真实场景中应该如何去选择呢,下面给出几种建议,希望对你有帮助:

  1. 如果你的堆大小不是很大(比如 100MB),选择串行收集器一般是效率最高的。

    参数:-XX:+UseSerialGC

  2. 如果你的应用运行在单核的机器上,或者你的虚拟机核数只有单核,选择串行收集器依然是合适的,这时候启用一些并行收集器没有任何收益。

    参数:-XX:+UseSerialGC

  3. 如果你的应用是“吞吐量”优先的,并且对较长时间的停顿没有什么特别的要求。选择并行收集器是比较好的。

    参数:-XX:+UseParallelGC

  4. 如果你的应用对响应时间要求较高,想要较少的停顿。甚至 1 秒的停顿都会引起大量的请求失败,那么选择G1ZGCCMS都是合理的。虽然这些收集器的 GC 停顿通常都比较短,但它需要一些额外的资源去处理这些工作,通常吞吐量会低一些。

    参数:

    -XX:+UseConcMarkSweepGC

    -XX:+UseG1GC

    -XX:+UseZGC 等。

从上面这些出发点来看,我们平常的 Web 服务器,都是对响应性要求非常高的。选择性其实就集中在 CMSG1ZGC上。而对于某些定时任务,使用并行收集器,是一个比较好的选择。

总结

回顾文章前面的目录

这几点你学会了吗?

(0)

相关推荐

  • 7种jvm垃圾回收器,这次全部搞懂

    前言 之前我们讲解了jvm的组成结构与垃圾回收算法等知识点,今天我们来讲讲jvm最重要的堆内存是如何使用垃圾回收器进行垃圾回收,并且如何使用命令去配置使用这些垃圾回收器. 堆内存详解 上面这个图大家应 ...

  • JVM调优之垃圾定位、垃圾回收算法、垃圾处理器对比

    谈垃圾回收器之前,要先讲讲垃圾回收算法,以及JVM对垃圾的认定策略,JVM垃圾回收器是垃圾回收算法的具体实现,了解了前面的前置知识,有利于对垃圾回收器的理解. 什么是垃圾? 垃圾,主要是指堆上的对象, ...

  • JVM 经典垃圾收集器

    本文部分摘自<深入理解 Java 虚拟机第三版> 概述 如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的实践者.Java 虚拟机规范中对垃圾收集器的实现做出规定,因此不同的厂 ...

  • JVM总结

    JVM总结

  • Java虚拟机垃圾回收(三) 7种垃圾收集器

    主要特点 应用场景 设置参数 基本运行原理 在<Java虚拟机垃圾回收(一) 基础>中了解到如何判断对象是存活还是已经死亡?在<Java虚拟机垃圾回收(二) 垃圾回收算法>了解 ...

  • JVM真香系列:如何判断对象是否可被回收?

    回复"000"获取大量电子书 在JVM中程序寄存器.Java虚拟机栈.本地方法栈,这三个区是随着线程的创建而创建,随着线程结束而销毁. 其实就是这三个的生命周期和线程的生命周期一样 ...

  • JVM真香系列:堆内存详解

    回复"000"获取大量电子书 前面的文章中已经有所提到过堆,只是大致介绍了一下.本文就来详细聊聊JVM中的堆. 在 JVM中,堆被划分成两个不同的区域:新生代 ( Young ). ...

  • JVM真香系列:方法区、堆、栈之间到底有什么关系

    回复"000"获取大量电子书 栈指向堆 如果在栈帧中有一个变量,类型为引用类型,比如: package com.tian.my_code.test; public class Jv ...

  • JVM真香系列:轻松掌握JVM运行时数据区

    回复"000"获取大量电子书 前面我们讲了从java源文件到class文件,在从class文件到JVM.那么今天继续聊JVM是如何布局的. JVM运行时数据区有几个?看看官网是就知 ...

  • JVM真香系列:轻松理解class文件到虚拟机(下)

    回复"000"获取大量电子书 类加载器 类加载器是很多人认为很硬的骨头.其实也没那么可怕,请听老田慢慢道来. 在装载(Load)阶段,通过类的全限定名获取其定义的二进制字节流,需要 ...

  • JVM真香系列:轻松理解class文件到虚拟机(上)

    回复"000"获取大量电子书 JVM初探 class文件到JVM中,就相当于我们吃饭,食物吃进了肚子里,不同的营养成分被身体不同的器官吸收. 查找class文件并导入到JVM中 ( ...

  • JVM真香系列:.java文件到.class文件

    回复"000"获取大量电子书 认识JVM 什么是JVM JVM 全称 Java Virtual Machine,也就是我们耳熟能详的 Java 虚拟机.它能识别 .class后缀的 ...

  • 真香系列之盗版的WPS?

    ‍‍ ‍‍逃离工地后明显觉得能写的素材少了. 我琢磨了下原因,发现是因为到格子间后,面对的就那几个固定的同事,故事有限:有趣程度也远比不上工地上的那些个吃喝嫖赌抽.吃拿卡要偷的主儿. 这就让我有点伤脑 ...

  • 10万元左右就能买!这款合资SUV实锤"真香"系列

    如何在有限的预算下,购买一辆称心如意的新座驾?成为当前年轻消费者普遍的难题.以10-15万级别的SUV为例,这里面既有主打高配置的国产SUV,也有主打品牌和品质的合资SUV,到底哪款车最值得年轻人入手 ...