JVM系列文章目录

初识JVM

深入理解JVM内存区域

玩转JVM对象和引用

JVM分代回收机制和垃圾回收算法

细谈JVM垃圾回收与部分底层实现

Class文件结构及深入字节码指令

玩转类加载和类加载器

方法调用的底层实现

Java语法糖及底层实现

GC调优基础知识工具篇之JDK自带工具

GC调优基础知识工具篇之Arthas与动态追踪技术

JVM调优之内存优化与GC优化

JVM调优之预估调优与问题排查

JVM调优之玩转MAT分析内存泄漏

直接内存与JVM源码分析

JVM及时编译器


细谈JVM垃圾回收与部分底层实现

  • JVM系列文章目录
  • 前言
  • 并发标记与三色标记
    • 三色标记
    • 漏标的原因
    • CMS中解决漏标问题的方案
    • G1中解决漏标问题的方案
    • G1和CMS解决漏标问题的区别
  • G1中的部分技术细节
    • G1的内存区域不固定
    • 跨代引用与CradTable、Rset
      • CradTbale
      • Rset
  • 安全点和安全区域
    • 安全点
    • 安全区域
  • 低延迟的垃圾回收器
    • 垃圾回收器三项指标
    • Eplison
    • ZGC
    • Shenandoah
  • GC相关
    • GC 日志详解
    • GC参数设置

前言

本文基于JDK1.8,是博主个人的JVM学习记录,欢迎各位指正错误的地方


并发标记与三色标记

在前面对文章中我们有聊到CMS垃圾回收器和G1垃圾回收器标记阶段都有并发标记到过程,并发标记的实现就要依赖这个三色标记。

三色标记

先来介绍一下三色标记的三种颜色代表的意思

  • 黑色:表示根对象,或者该对象及其子对象已被扫描过了;
  • 灰色:表示该对象已被扫描了,但是子对象还未被扫描到;
  • 白色:该对象还未被扫描;

通过这种三色标记,就可以多个线程同时标记,不用担心彼此标记被影响,在垃圾回收的时候只要回收白色的对象就可以;但是三色标记也是有缺陷的,它会出现漏标的问题。

漏标的原因

我们通过图解的方式来大致介绍一下漏标的过程:
假设我们有多个线程在进行GC回收的并发标记

上图可以看出线程1已经完成基本的扫描了,只要等最后回收就行了,但是
B对象对C对象的引用发生了变化,B对象不引用C对象了,改为A对象引用C对象了,C对象按理来说也是根可达的,应该被扫描并标记为黑色,但是线程1已经完成扫描了,单独依赖三色标记法没办法解决C漏标的问题。


为解决这个问题,在CMS和G1中分别提供了不用的解决方案。

CMS中解决漏标问题的方案

在CMS中使用Incremental Update 算法 来解决三色标记导致的漏标问题。大致思路是这样的:当一个白色对象黑色对象引用时,将这个黑色对象标记为灰色对象,并让线程重新从根对象开始扫描。因为有从根对象重新扫描的关系,所以速度比较慢。

G1中解决漏标问题的方案

G1使用SATB(snapshot-at-the-beginning),在开始的时候做一个快照,当上图中B、C之间的引用发生变化时,直接将这个引用推到GC的堆栈中(没错,GC也使用堆栈,GC方法运行时数据也是来自栈中的,GC本质也是线程,也要占用一小部分的堆栈,我们也可以将它当作一个普通java线程来理解就行了),这样我下次扫描(最终标记的时候)的时候直接就能扫到C对象。

G1和CMS解决漏标问题的区别

  1. CMS关注引用的增加,关注的是A到C新增这条引用;G1关注引用的删除,关注B到C减少的这条引用。
  2. CMS重新扫描要从根对象开始扫,STW时间长,G1只要从变更的引用对象开始扫就可以了,STW时间短,更高效。

G1中的部分技术细节

G1的内存区域不固定

因为G1使用的是一个个Region,每个Region都可摇身一变在Eden、Survivor、Old、Humongous之间来回切换身份(这个在JVM分代回收机制和垃圾回收算法有介绍),通过这种切身份的方式可以让垃圾回收更高效,比如说Region开始是Eden,发生新生代垃圾回收后,直接在自己内部复制删除,然后把自己变成Survivor就行了。

跨代引用与CradTable、Rset

跨代引用一般指的是老年代跨代对象引用新生代的对象,这个时候这个新生代没办法确定这个对象是不是死了,但是又不可能扫描整个整个老年代(扫描整个老年代效率也太低了吧),所以G1通过CradTbale(卡表)和Rset(记忆集)来解决这个跨代引用的问题。

CradTbale

  • CradTable实际上就是一个数组,它每一个下表都代表堆中间一片连续的内存区域,比如crad[0]表示为 0x0000~0x03FF这片内存区域、crad[1]0x0400 ~ 0x07FF。
  • CradTable数组只有两种值:0/1,0表示该区域没有跨代引用,1表示该区域有一个或多个跨代引用。这样垃圾回收器在垃圾回收的时候只要根据下表扫描对应一小片区域的对象就可以了。
  • CradTable在堆中只需要一份,它可以记录整个堆的跨代引用情况。

Rset

  • Rset实际上就是记录引用的集合(记录了其他 Region 中的对象到本 Region 的引用)。
  • 它可以理解成一个key-value的格式,前面的key表示的对应的CradTable的数组下表,value存储的是引用,这样谁引用了当前Region中的对象,直接扫描Rset就可以了。
  • ReSet本身就是一个Hash表,如果是在 G1 的话,则是在一个新生代的 Region 区里面,这样的话其实内存消耗很大的,多的时候能达到JVM内存空间的20%,所以G1真的不是小内存的机器玩得起的(CMS也有类似处理)。

安全点和安全区域

在介绍安全点和安全区域的之前先声明一点,JVM的GC不是强制性的(不是JVM要垃圾回收了,直接让业务线程统统挂起),它是主动式的(主动指的是业务线程,业务线程自己乖乖挂起),通常是GC线程设置一个状态值,告诉业务线程我要收垃圾了,业务线程回去轮询这个状态值,发现GC要收垃圾了,而自己刚好又处于安全点,就进行GC。

安全点

安全点其实可以理解成业务线程暂停的时候对象引用不会发生变化的节点(确保用户线程暂停的这行字节码指令是不会导致引用关系的变化)。比如方法调用、循环跳转、异常跳转等,一般是这些指令才会产生安全点。

安全区域

需要安全区域这个概念的原因是:有的时候业务线程可能不在运行(业务线程处于 Sleep 或者是 Blocked 状态),业务线程没办法自己到达安全点。
安全区域是指能够确保在某一段代码片段之中, 引用关系不会发生变化,因此,在这个区域中任意地方开始垃圾收集都是安全的。我们也可以把安全区域看作被扩展拉伸了的安全点。
如果业务线程在垃圾回收中途“醒了”,它也要确定 JVM 是否已经完成了根节点枚举,或者其他 GC 中需要暂停用户线程的阶段。


低延迟的垃圾回收器

垃圾回收器三项指标

传统的垃圾回收器一般只能在内存占用、吞吐量、延时 同时满足两个。但是现在的发展,延迟这项的目标越来越重要。所以就有低延迟的垃圾回收器。

Eplison

  • 这个垃圾回收器不能进行垃圾回收,是一个“不干活”的垃圾回收器,由 RedHat 推出。
  • 它负责堆的管理与布局、对象的分配、与解释器 的协作、与编译器的协作、与监控子系统协作等职责,主要用于需要剥离垃圾收集器影响的性能测试和压力测试 (不是大佬真的用不来)。

ZGC

  • 有类似于 G1 的 Region,但是没有分代。
  • 标志性的设计是染色指针 ColoredPointers,染色指针有 4TB 的内存限制,但是效率极高,它是一种将少量额外的信息存储在指针上 的技术。 它可以做到几乎整个收集过程全程可并发,短暂的 STW 也只与 GC Roots 大小相关而与堆空间内存大小无关,因此考科一实现任何堆空间 STW 的时间小于 十毫秒的目标(这个收费的,大部分公司也用不上)。

Shenandoah

  • 第一款非 Oracle 公司开发的垃圾回收器,有类似于 G1 的 Region, 但是没有分代。
  • 也用到了染色指针 ColoredPointers。 效率没有 ZGC 高,大概几十毫秒的目标。

GC相关

GC 日志详解

这里粗略介绍一下日志内容

GC参数设置

GC 常用参数-Xmn -Xms -Xmx –Xss 年轻代 最小堆 最大堆 栈空间-XX:+UseTLAB 使用 TLAB,默认打开-XX:+PrintTLAB 打印 TLAB 的使用情况-XX:TLABSize 设置 TLAB 大小 -XX:+DisableExplicitGC 启用用于禁用对的调用处理的选项 System.gc()-XX:+PrintGC 查看 GC 基本信息-XX:+PrintGCDetails 查看 GC 详细信息-XX:+PrintHeapAtGC 每次一次 GC 后,都打印堆信息-XX:+PrintGCTimeStamps 启用在每个 GC 上打印时间戳的功能-XX:+PrintGCApplicationConcurrentTime 打印应用程序时间(低)-XX:+PrintGCApplicationStoppedTime 打印暂停时长(低)-XX:+PrintReferenceGC 记录回收了多少种不同引用类型的引用(重要性低)-verbose:class 类加载详细过程-XX:+PrintVMOptions 可在程序运行时,打印虚拟机接受到的命令行显示参数-XX:+PrintFlagsFinal -XX:+PrintFlagsInitial 打印所有的 JVM 参数、查看所有 JVM 参数启动的初始值(必须会用)-XX:MaxTenuringThreshold 升代年龄,最大值 15, 并行(吞吐量)收集器的默认值为 15,而 CMS 收集器的默认值为 6。Parallel 常用参数-XX:SurvivorRatio-XX:PreTenureSizeThreshold-XX:MaxTenuringThreshold-XX:+ParallelGCThreads-XX:+UseAdaptiveSizePolicyCMS常用参数-XX:+UseConcMarkSweepGC-XX:+ParallelGCThreads设置伊甸园空间大小与幸存者空间大小之间的比率。默认情况下,此选项设置为 8大对象到底多大,大于这个值的参数直接在老年代分配 升代年龄,最大值 15, 并行(吞吐量)收集器的默认值为 15,而 CMS 收集器的默认值为 6。 并行收集器的线程数,同样适用于 CMS,一般设为和 CPU 核数相同 自动选择各区大小比例启用 CMS 垃圾回收器 并行收集器的线程数,同样适用于 CMS,一般设为和 CPU 核数相同 -XX:CMSInitiatingOccupancyFraction 收)-XX:+UseCMSCompactAtFullCollection-XX:CMSFullGCsBeforeCompaction-XX:+CMSClassUnloadingEnabled-XX:CMSInitiatingPermOccupancyFraction使用多少比例的老年代后开始 CMS 收集,默认是 68%(近似值),如果频繁发生 SerialOld 卡顿,应该调小,(频繁 CMS 回在 FGC 时进行压缩 多少次 FGC 之后进行压缩 使用并发标记扫描(CMS)垃圾收集器时,启用类卸载。默认情况下启用此选项。 达到什么比例时进行 Perm 回收,JDK 8 中不推荐使用此选项,不能替代。-XX:GCTimeRatio-XX:MaxGCPauseMillisG1 常用参数设置 GC 时间占用程序运行时间的百分比(不推荐使用) 停顿时间,是一个建议时间,GC 会尝试用各种手段达到这个时间,比如减小年轻代-XX:+UseG1GC 启用 CMS 垃圾收集器-XX:MaxGCPauseMillis 设置最大 GC 暂停时间的目标(以毫秒为单位)。这是一个软目标,并且 JVM 将尽最大的努力(G1 会尝试调整 Young 区的块数来)来实 现它。默认情况下,没有最大暂停时间值。-XX:GCPauseIntervalMillis GC 的间隔时间-XX:+G1HeapRegionSize 分区大小,建议逐渐增大该值, 1 2 4 8 16 32。随着 size 增加,垃圾的存活时间更长, GC 间隔更长,但每次 GC 的时间也会更长-XX:G1NewSizePercent 新生代最小比例,默认为 5%-XX:G1MaxNewSizePercent 新生代最大比例,默认为 60%-XX:GCTimeRatioGC 时间建议比例,G1 会根据这个值调整堆空间-XX:ConcGCThreads 线程数量-XX:InitiatingHeapOccupancyPercent 启动 G1 的堆空间占用比例,根据整个堆的占用而触发并发 GC 周期

上一篇:JVM分代回收机制和垃圾回收算法

下一篇:Class文件结构及深入字节码指令

细谈JVM垃圾回收与部分底层实现相关推荐

  1. 【搞定Jvm面试】 JVM 垃圾回收揭秘附常见面试题解析

    JVM 垃圾回收 写在前面 本节常见面试题 问题答案在文中都有提到 如何判断对象是否死亡(两种方法). 简单的介绍一下强引用.软引用.弱引用.虚引用(虚引用与软引用和弱引用的区别.使用软引用能带来的好 ...

  2. 【JAVA进阶】JVM第二篇- JVM 垃圾回收详解

    写在前面的话 脑子是个好东西,可惜的是一直没有搞懂脑子的内存删除机制是什么,所以啊,入行多年,零零散散的文章看了无数,却总是学习了很多也忘了很多. 痛定思痛的我决定从今天开始系统的梳理下知识架构,记录 ...

  3. JVM虚拟机(四):JVM 垃圾回收机制概念及其算法

    垃圾回收概念和其算法 谈到垃圾回收(Garbage Collection)GC,需要先澄清什么是垃圾,类比日常生活中的垃圾,我们会把他们丢入垃圾箱,然后倒掉.GC中的垃圾,特指存于内存中.不会再被使用 ...

  4. java学习笔记-4 JVM垃圾回收(GC)

    引言 jvm垃圾回收相关的问题是老生常谈的问题了,相信大家都有所了解,这里再进行相关的探讨,以加深理解.若文中有不正之言,望不吝指正. 本文将围绕以下几个点展开 1.为什么要进行垃圾回收 我们知道jv ...

  5. jvm对象从新生代到老年代_JVM内存管理、JVM垃圾回收机制、新生代、老年代以及永久代...

    内存模型 JVM运行时数据区由程序计数器.堆.虚拟机栈.本地方法栈.方法区部分组成,结构图如下所示. JVM内存结构由程序计数器.堆.栈.本地方法栈.方法区等部分组成,结构图如下所示: 1)程序计数器 ...

  6. JVM内存区域(Java内存区域)、JVM垃圾回收机制(GC)初探

    一.JVM内存区域(Java内存区域) 首先区分一下JVM内存区域(Java内存区域)和Java内存模型(JMM)的概念.Java线程之间的通信采用的是共享内存模型,这里提到的共享内存模型指的就是Ja ...

  7. java jvm垃圾回收算法_深入理解JVM虚拟机2:JVM垃圾回收基本原理和算法

    本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 喜欢的话麻烦点下Star哈 文章将同步到我的个人博客: www.how ...

  8. 读书笔记之JVM垃圾回收

    前言 ​ 如果要问Java与其他编程语言最大的不同是什么,我第一个想到的一定就是Java所运行的JVM所自带的自动垃圾回收机制,以下是我学习JVM垃圾回收机制整理的笔记,希望能对读者有一些帮助. 哪些 ...

  9. 深入理解JVM虚拟机2:JVM垃圾回收基本原理和算法

    JVM GC基本原理与GC算法 Java的内存分配与回收全部由JVM垃圾回收进程自动完成.与C语言不同,Java开发者不需要自己编写代码实现垃圾回收.这是Java深受大家欢迎的众多特性之一,能够帮助程 ...

最新文章

  1. python使用正则表达式抽取字符串中最大数值数字
  2. java 注解报错_java注解验证接收参数 返回注解字段的错误
  3. NLP:GLUE和SuperGLUE基准的简介、任务分类、使用方法之详细攻略
  4. Web(浏览器)打开运行WinForm应用程序
  5. CentOS 8下安装MySQL8.0
  6. 关于 angularjs 的小结
  7. Dede更新提示DedeTag Engine Create File False的解决办法
  8. java datetime 转换_如何实现Java日期时间格式转换
  9. python人门指南小说-致Python初学者 Anaconda入门使用指南完整版
  10. ZooKeeper食谱(八)
  11. C语言入门经典(第5版)
  12. Shell语法中的循环
  13. CRM客户关系管理系统
  14. Spring的加密工具类---DigestUtils
  15. 不能创建对象qmdispatch_关于系统弹出错误:429 , ActiveX 部件不能创建对象 的解决方法...
  16. 如何做思维导图?用这两个软件就可以了
  17. 我为什么要选软件工程专业
  18. 基于spring boot的婚纱摄影约拍系统
  19. 你了解PMP考试新考纲的内容吗?
  20. Java一瓶可乐_Java实现可乐瓶问题

热门文章

  1. H5网页使用支付宝授权登录获取用户信息详解
  2. hnu 模型机组合部件的实现(一)
  3. u深度重装系统详细教程_u深度u盘启动盘安装win7详细步骤
  4. 加油站踩踏式逃亡?电网成大赢家?时代抛弃你的时候,真的一声不吭
  5. 用计算机打青春不打烊,彩色的青春不打烊
  6. Windows电脑上最好的3个epub阅读器
  7. win10台式机插入耳机检测不出来
  8. [汇编] 汇编语言实现简易文本编辑器(光标移动、上卷和退格删除)
  9. Games101 学习笔记
  10. linux 流量控制 1