作为使用范围最广的虚拟机之一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. im2col:将卷积运算转为矩阵相乘

    目录 im2col实现 优缺点分析 参考 博客:blog.shinelee.me | 博客园 | CSDN im2col实现 如何将卷积运算转为矩阵相乘?直接看下面这张图,以下图片来自论文High P ...

  2. MIP 组件库升级公告

    背景 为了方便广大开发者们更好地使用组件,现对 MIP 组件库统一进行升级,升级后将分为通用组件和自定义组件两类,方便开发. 通用组件,即考虑了组件通用性方便所有开发者参考使用的组件,将存放于 mip ...

  3. Java8新特性之五:Optional

    NullPointerException相信每个JAVA程序员都不陌生,是JAVA应用程序中最常见的异常.之前,Google Guava项目曾提出用Optional类来包装对象从而解决NullPoin ...

  4. 带着新人看java虚拟机01

    1.前言(基于JDK1.7) 最近想把一些java基础的东西整理一下,但是又不知道从哪里开始!想了好久,还是从最基本的jvm开始吧!这一节就简单过一遍基础知识,后面慢慢深入... 水平有限,我自己也是 ...

  5. C#开发APP,ToolBar控件在Smobiler中的使用方式【附案例源码】——Smobiler移动开发平台

    控件说明 底部工具栏控件. 效果演示 其他效果 该界面为仿淘宝UI制作的一个简单的UI模板,源码获取方式请拉至文章末尾. 特色属性 属性 属性说明 Direction(相对布局) 容器主轴方向. Fl ...

  6. Oracle数据库常用SQL函数

    1.SQL函数的概念: 函数一般是在数据上执行的,它给数据的转换和处理提供了方便.只是将取出的数据进行处理,不会改变数据库中的值.(类似于java中的方法但函数只是将数据库中的数据取出(复制)到函数中 ...

  7. 结合Mybatis源码看设计模式——外观模式

    定义 提供了一个统一的接口,用来访问子系统中一群接口 适用场景 子系统复杂,增加外观模式提供简单调用接口 构建多层系统结构,用外观对象作为每层入口 详解 外观模式,主要理解外观.通俗一点可以认为这个模 ...

  8. vue之$root,$parent

    $root vue状态管理使用vuex,如果项目不大,逻辑不多,name我们没必要用vuex给项目增加难度,只需要用$root设置vue实例的data就行了,如下 main.js new Vue({ ...

  9. 24, CSS 构造超链接

    1. 超链接边框 2. 派生超链接 3. 属性选择器超链接 4. 动态超链接 5. 图像翻转超链接 6. CSS 工具提示 1.给链接加上边框 A:link { Color: #f00; Text-d ...

  10. MAC中使用APICloud同步代码错误解决办法

    在MAC上使用APICloud同步代码时出现错误,其实就是git位置的问题,简单点就是把路径映射下. 问题提示: Can't locate SVN/Core.pm in @INC (you may n ...