作为使用范围最广的虚拟机之一HotSpot,必须对垃圾回收算法的执行效率有严格的考量,只有这样才能保证虚拟机高效运行

枚举根节点

从可达性分析中从 GC Roots 节点找引用链这个操作为例,可以作为 GC Roots 的节点主要在全局性的引用(例如常量或者类静态属性)与执行上下文(例如栈帧中的本地变量表)中。

但是现在很多应用仅仅方法区就有数百兆,如果要逐个检查这里面的引用,那么必然会消耗很多的时间。

另外,可达性分析对执行时间的敏感还体现在 GC 停顿上,因为这项分析工作必须在一个能确保一致性的快照中进行 —— 这里的“一致性”指的是在整个分析过程中整个执行系统看起来就像是被冻结在某个时间点上,不可以出现分析过程中对象引用关系还在不停变化的情况,该点不满足的话分析结果准确性就无法得到保证。

这点是导致 GC 进行时必须停顿所有 Java 执行线程的其中一个重要原因,即使是号称不会发生停顿的 CMS 收集器中,枚举根节点也是必须要停顿的。

由于目前主流 Java 虚拟机使用的都是准确式 GC,所以当执行系统停顿下来后,并不需要一个不漏的检查完成所有执行上下文和全局的引用位置,虚拟机应当是有办法直接得知那些地方存放着对象引用的。 在HotSpot的实现中,是使用一组被称为OopMap的数据结构来达到这个目的的,在类加载的时候,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT 编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。这样, GC 在扫描时就可以直接得知这些信息了。

安全点

在 OopMap 的帮助下,HotSpot 可以快速并且准确的完成 GC Roots 枚举,但是一个很现实的问题随之而来:可能导致引用关系变化,或者说 OopMap 内容变化的指令非常多,如果为每一条指令都生成对应的 OopMap,那么将需要大量的额外空间,这样 GC 的空间成本将会变得很高。

实际上,HotSpot 也的确没有为每一条指令都生成 OopMap,只是在“特定的位置” 记录了这些信息,这些位置被称为是安全点,即程序执行时并非是在所有地方都能停顿下来开始 GC ,只有到达安全点时才能暂停。

安全点的选择既不能太少以至于让 GC 等待太长时间,也不能过于频繁以至于过分增大运行时负荷。

所以,安全点的选定基本上是以程序“是否具有让程序长时间执行的特征”为标准进行选定的——因为每条指令执行的时间都非常短暂,程序不太可能因为指令流长度太长这个原因而过长时间运行,“长时间执行”的最明显特征就是指令序列复用,例如方法调用、循环跳转、异常跳转等,所以具有这些功能的指令才会产生安全点。

对于安全点,另一个需要考虑的问题就是如何让 GC 发生时让所有线程(这里不包括执行 JNI 调用的线程)都“跑”到最近的安全点再停顿下来。这里有两种方案可供选择:抢先式中断和主动式中断。

  • 抢先式中断:无需线程的执行代码主动配合,在 GC 发生时,首先把所有线程全部中断,如果发现有线程中断的地方不再安全点上,就会发线程,让它“跑”到安全点上。现在几乎没有虚拟机实现采用抢先式中断来暂停线程从而响应GC事件
  • 主动式中断:当 GC 需要中断线程时,不直接对线程操作,仅仅简单地设置一个标志,各个线程执行时主动去轮询这个标志,发现中断标志为真时就自己中断挂起。轮询标志的地方和安全点是重合的,另外再加上创建对象需要分配内存的地方。

安全区域

使用安全点似乎已经完美解决了如何进入 GC 的问题,但实际情况却并不一定,安全点机制保证了程序执行,在不太长的时间内就会遇到可以进入 GC 的安全点。

但是线程“不执行”的时候呢?所谓不执行就是没有分配 CPU 时间,典型的例子就是线程处于 Sleep 状态或者 Blocked状态,这时候线程无法响应 JVM 的中断请求,“走”到安全点去中断挂起,JVM显然也不太可能等待线程重新被分配 CPU 时间。对于这种状况,就需要安全区域来解决。

安全区域就是在一段代码片段中,引用关系不会发生变化,在这个区域中的任意地方开始 GC 都是安全的。

在线程执行到安全区域中的代码时,首先标识自己已经进入了安全区域,那样,当这段时间里 JVM 要发起 GC 时,就不用管标识自己为安全区域状态的线程了。

当线程要离开安全区域时,它要检查系统是否已经完成了根节点枚举(或者是整个 GC 过程),如果完成了,那线程就继续执行,否则它就必须等待直到收到可以安全离开安全区域的信号为止。

HotSpot 虚拟机垃圾回收算法实现的更多相关文章

  1. HotSpot的垃圾回收算法

    这系列文章只简单介绍一下HotSpot垃圾回收中涉及到的算法及相关的垃圾回收器,并不进行源代码分析,后面会开一个系列对HotSpot的垃圾回收以及内存管理进行源代码解读. 涉及到的垃圾回收算法一共有 ...

  2. JAVA虚拟机垃圾回收算法原理

    除了释放不再被引用的对象外,垃圾收集器还要处理堆碎块.新的对象分配了空间,不再被引用的对象被释放,所以堆内存的空闲位置介于活动的对象之间.请求分配新对象时可能不得不增大堆空间的大小,虽然可以使用的总空 ...

  3. java虚拟机-垃圾回收算法

    在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM来处理.但是首先需要明确,什么样的对象才能当为垃圾: 1.引用计数法:如果某个引用(即指针)指向对象,那么说明该对象还 ...

  4. 【译】Java SE 14 Hotspot 虚拟机垃圾回收调优指南

    原文链接:HotSpot Virtual Machine Garbage Collection Tuning Guide,基于Java SE 14. 本文主要包括以下内容: 优化目标与策略(Ergon ...

  5. Java虚拟机—垃圾回收算法(整理版)

    1.概述 由于垃圾收集算法的实现涉及大量的程序细节.因此本节不打算过多地讨论算法的实现,只是介绍几种算法的思想及其发展过程.主要涉及的算法有标记-清除算法.复制算法.标记-整理算法.分代收集算法. 2 ...

  6. 了解java虚拟机—垃圾回收算法(5)

    引用计数器法(Reference Counting) 引用计数器的实现很简单,对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器减1.只要对象A的引用计数器的 ...

  7. 深入理解java虚拟机【垃圾回收算法】

    Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...

  8. (转)《深入理解java虚拟机》学习笔记3——垃圾回收算法

    Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...

  9. Java虚拟机运行时数据区域及垃圾回收算法

    程序计数器 记录正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法则为空). Java 虚拟机栈 每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表.操作数栈.动态链接.方法出口 ...

随机推荐

  1. SOLID原则(OOD&OOP)

    SOLID原则是面向对象编程和面向对象设计的头五大原则.学习及应用这五大原则可以构建一个易于维护和扩展的应用程序,我们一起看看到底是那五大原则. S--单一责任原则(SRP) --Single Res ...

  2. 重磅!!!微软发布.NET Core 2.2

    我们很高兴地宣布发布.NET Core 2.2.它包括对运行时的诊断改进,对ARM32 for Windows和Azure Active Directory for SQL Client的支持.此版本 ...

  3. PHP学习笔记 01 之表单传值

    一.HTML传值/PHP接收方法 1.GET(地址栏+问号+数据信息) (1)方式一:表单Form: method = 'get' GET接收数据方式: $_GET['表单元素name对应的值] (2 ...

  4. Failed to fetch URL http://dl-ssl.google.com/android/repository/addons_list-2.xml, reason:

    http://blog.csdn.net/gyming/article/details/8168166/ 最近接受的这个项目需要Android SDK Tools revision 22.6.2 or ...

  5. Springcloud Gateway 路由管理

    Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开 ...

  6. 直观理解神经网络最后一层全连接+Softmax

    目录 写在前面 全连接层与Softmax回顾 加权角度 模板匹配 几何角度 Softmax的作用 总结 参考 博客:blog.shinelee.me | 博客园 | CSDN 写在前面 这篇文章将从3 ...

  7. 数据结构与算法(十):红黑树与TreeMap详细解析

    本文目录 一.为什么要创建红黑树这种数据结构 在上篇我们了解了AVL树,既然已经有了AVL这种平衡的二叉排序树,为什么还要有红黑树呢? AVL树通过定义我们知道要求树中每一个结点的左右子树高度差的绝对 ...

  8. eclipse中运行出错:无法初始化主类的解决办法

    问题描述:eclipse中运行程序时,出现如下错误 解决办法: 出现此类:无法初始化主类有可能是因为eclipse中Java的版本与JDK的版本不匹配,我开始用的时候eclipse中用的是Java s ...

  9. supervisord 备注

    最近项目中使用了supervisord,简单做下备注. supervisord是linux下基于python开发的一个服务管理工具,类似之前node环境下的forever,用该方法启动进程后,supe ...

  10. linux shell 备注(一)

    1.特殊字符 #!/bin/bash # $表示当前PID ID echo $$ # $n是shell脚本的参数,当0是第一个参数,即文件名 # $#是shell当前脚本的参数个数 # 例如:sh03 ...