HotSpot 虚拟机垃圾回收算法实现
作为使用范围最广的虚拟机之一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 虚拟机垃圾回收算法实现的更多相关文章
- HotSpot的垃圾回收算法
这系列文章只简单介绍一下HotSpot垃圾回收中涉及到的算法及相关的垃圾回收器,并不进行源代码分析,后面会开一个系列对HotSpot的垃圾回收以及内存管理进行源代码解读. 涉及到的垃圾回收算法一共有 ...
- JAVA虚拟机垃圾回收算法原理
除了释放不再被引用的对象外,垃圾收集器还要处理堆碎块.新的对象分配了空间,不再被引用的对象被释放,所以堆内存的空闲位置介于活动的对象之间.请求分配新对象时可能不得不增大堆空间的大小,虽然可以使用的总空 ...
- java虚拟机-垃圾回收算法
在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM来处理.但是首先需要明确,什么样的对象才能当为垃圾: 1.引用计数法:如果某个引用(即指针)指向对象,那么说明该对象还 ...
- 【译】Java SE 14 Hotspot 虚拟机垃圾回收调优指南
原文链接:HotSpot Virtual Machine Garbage Collection Tuning Guide,基于Java SE 14. 本文主要包括以下内容: 优化目标与策略(Ergon ...
- Java虚拟机—垃圾回收算法(整理版)
1.概述 由于垃圾收集算法的实现涉及大量的程序细节.因此本节不打算过多地讨论算法的实现,只是介绍几种算法的思想及其发展过程.主要涉及的算法有标记-清除算法.复制算法.标记-整理算法.分代收集算法. 2 ...
- 了解java虚拟机—垃圾回收算法(5)
引用计数器法(Reference Counting) 引用计数器的实现很简单,对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器减1.只要对象A的引用计数器的 ...
- 深入理解java虚拟机【垃圾回收算法】
Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...
- (转)《深入理解java虚拟机》学习笔记3——垃圾回收算法
Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...
- Java虚拟机运行时数据区域及垃圾回收算法
程序计数器 记录正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法则为空). Java 虚拟机栈 每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表.操作数栈.动态链接.方法出口 ...
随机推荐
- 从壹开始前后端分离 39 || 想创建自己的dotnet模板么?看这里
缘起 开工是利啦!哈喽各位小伙伴,周三好呀,新的一年又开始了,老张给大家做个榜样,新的一年也要好好学习哟,这两天闲的无事就整理整理了这个系列的 Github 代码,增加了一个英文的 README ,然 ...
- 【重学计算机】操作系统D5章:文件系统
1. 文件系统 文件系统概述 文件的组织: 逻辑结构:流式.记录式 物理结构:顺序.连接.直接.索引 文件的存取:顺序.直接.索引 文件的控制:逻辑控制.物理控制 文件的使用:打开.关闭.读.写.控制 ...
- 更好用的css命名方式——BEM命名
一.什么是BEM? BEM代表块(Block),元素(Element),修饰符(Modifier).无论是什么网站页面,都可以拆解成这三部分. 二.带你认识网页 我们来看一下qq的官网,它可以由三个块 ...
- Unity C#笔记 协程
什么是协程 协同程序,在主程序运行的同时,开启另外一段逻辑处理,来协同当前程序的执行. 可能看了这段文字介绍还是有点模糊,其实可以用多线程来比较. 多线程 多线程,顾名思义,多条同时执行的线程. 最初 ...
- Dotnet全平台下APM-Trace探索
背景 随着支撑的内部业务系统越来越多,向着服务化架构进化,在整个迭代过程中,会逐渐暴露出以下问题. 传统依赖于应用服务器日志等手段的排除故障原因的复杂度越来越高,传统的监控服务已经无法满足需求. 终端 ...
- 天津联通新兴ICT业务工程师面试经历
此次是天津联通来我们学校进行校招宣讲,参加的人挺多的.一开始没打印成绩单,临时去打印的,然后排到我的时候以经快结束了 == 面试 首先当然是自我介绍啦,就巴拉巴拉了一堆自己的专业,学过什么跟职位相关的 ...
- Git 分支模型
翻译自:https://nvie.com/posts/a-successful-git-branching-model/ 在这篇文章中,主要介绍 Git 分支模型.不会谈论任何项目的细节,只讨论分支策 ...
- 对比 Git 与 SVN,这篇讲的很易懂
---恢复内容开始--- 欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯工蜂发表于云+社区专栏 导语 本文从 Git 与 SVN 的对比入手,介绍如何通过 Git-SVN 开始 ...
- CenOS_用户级别
1.运行级别 共有7个运行级别: 0:关机 1:单用户(不用密码也能登陆) 2:多用户无网络 3:多用户有网络 4:保留 5:图形化界面 6:重启 1.1设置运行级别语法: init <级别数字 ...
- ansible基础-task控制
1. 前言 很多情况下,一个play是否执行会依赖于某个(些)变量的值,这个变量可以来自自定义变量.facts,甚至是另一个task的执行结果. ansible通过变量判定task是否执行,我们称之为 ...