【译文】 GC 安全点 和安全区域
原文链接 : here
根引用 Root references
一个实例死了,意味着它变得无用。只用程序员知道一个实例是否已经无用。为了让程序知道一个实例是否已经无用,我们可以使用编译器分析,引用计数, 或者 可达性分析。
可达性分析假设只要一个实例是可达的,它就是活着的。如果一个实例的引用直接包含在当前函数栈的一个槽(slot)中,它就是直接可达的。那些被可达实例引用的实例也是可达的。因此,可达性分析就是找出那些直接可达的引用,也就是根引用。 根引用的集合就做根集合。
当前执行函数(the mutator)的上下文中 有直接可达的数据,因此 找根集合就是在上下文中找实例的引用。当前函数的上下文引用它的栈和寄存器文件(和一些线程所有的数据)。全局数据也是直接可达的。
枚举根集合Root set enumeration
一般情况下,如果GC使用可达性来确定一个实例是否存活,GC需要获得当前执行现场的一个一致的快照(a consistent snapshot),以便枚举根引用。 这对于stop-the-world 和 并发 GC(多数情况)都适用。 “一致” 意味着快照看起来就像在单个时间点上取得的。 一个一致的根引用快照对于正确性很有必要,否则一些活着的实例就可能丢失。 那现在的问题就是怎样获得当前上下文一致的快照。
为了获得一致的快照,一个简单的办法就是在枚举根引用的过程中,当前执行函数阻塞。 如果在枚举过程中,根集合是不变的,快照也是一致的。
当前执行函数阻塞了它的执行以后,它就不一定能够枚举它上下文中的根引用,除非它在它的上下文中登记并保存了那些引用信息。也就是说,它应该能够分辨出那个栈里有引用,哪个寄存器里有引用。 如果GC能够准确地获得这些信息,它就是准确根集合枚举, 否则就是模糊的。
(对应模糊枚举,GC使用启发式规则来保守地猜测哪些是上下文中的引用。因此,这些GC就是所谓的保守GC。 这篇文章只讨论准确枚举。)
Harmony 通过使用GC安全点和安全区域, 支持准确根集合枚举。
GC安全点和安全区域 Safe-point (or safepoint)
为了支持准确枚举,JIT编译器需要做一些额外的工作,因为只有JIT准确地知道栈帧信息和寄存器上下文。 当JIT编译一个方法的时候, 对于每一个指令, 它都保存根引用信息,以防执行阻塞在那个指令上。
但是对每一个指令都保存那些信息太昂贵了。 它需要大量空间保存那些信息。 这是不必要的,因为 只有一小部分指令有机会在实际执行时阻塞。 JIT只需要保存那部分指令的信息就够了-- 他们就把叫做安全点。安全点意味着对应根枚举来说,在该点阻塞是安全的。
顺便说一下,并不是所有编程语言的编译器都能够确切知道堆栈信息。 只有安全语言有这个能力。 比如, C/C++就没有。
函数阻塞 Mutator suspension
对于安全点,现在的问题是,我们怎么保证函数在安全点阻塞。
有两种基本方法来阻塞当前函数,抢先或者自愿。抢先式的方法是无论任何时候GC需要进行一次收集,它都立即阻塞当前函数的执行。当它发现函数被阻塞在一个不安全的点时,它会恢复函数执行,向前滚动到一个安全点。这种方法在ORP【1】中实现,它是Harmony的前一个版本。但是,目前几乎没有JVM使用这种方法。
在Harmony中实现的方法是自愿阻塞。当GC触发一次垃圾回收,它只是简单的设置一个标志; 当前执行函数会轮询这个标志, 当发现这个标志置位的时候,他们就会阻塞。那些轮询的点都是安全点。 多数情况下, JIT负责在合适的位置插入轮询程序。 有时,VM也需要有一些轮询点。
轮询点 Polling point
那么哪里是正确轮询GC触发事件的地点呢? 就像我们上面讨论的,我们不想在每一个指令处都设置轮询点。 对于自愿阻塞,一个更严重的问题是轮询负载。因此插入轮询点需要遵循一些基本原则:第一,轮询点应该足够频繁,以便GC不需要等待当前函数阻塞的时间太长, 因为其他函数还在等着GC释放空间来继续执行。第二,轮询点不能太多,导致增加运行负载过重。
最好的结果是刚好有足够的轮询点满足需求:
- 一类强制的轮询点是内存分配点。分配可以触发一次垃圾收集,因此分配必须是安全点
- 长时间的执行总是和方法调用或者循环 有关。因此,调用点和循环回边点也是期望的轮询点
这些就是Harmony中的轮询点: 分配点,调用点和循环回边点。 多数情况下运行时负载小于1%。 不幸的是,我们发现单独安全点是不够的。
安全区域 Safe-region
为什么不够呢? 因为我们忘掉了一种常见执行的情况。我们忘了它,因为它实际上不是长时间执行,而是长时间闲置。有这样一类情况程序不能及时响应GC触发事件,比如sleep,因系统调用阻塞。 这些操作不是JVM能够控制的。JVM在此期间不能够响应GC事件。 因此,我们引入了安全区域的概念来解决这个问题。
安全区域是其中引用不会改变的一段代码片段,那么在其中任一点进行根枚举都是安全的。 换句话说,安全区域是安全点的一个很大的扩展。
在安全点的设计中,如果GC触发事件发生了,执行函数通过轮询进行响应。它通过设置一个准备好的标志(ready flag)来响应。 那么GC就可以进行根枚举了。这是一个握手协议。
安全区域也遵循这个协议。执行函数在进入安全区域时设置ready flag。在它离开安全区域以前,它先检查GC是否完成了枚举(或者收集),并且不再需要执行函数呆在阻塞状态。如果是真的,它就向前执行,离开安全区域; 否则,它就像安全点一样阻塞他自己。
在Harmony的实现中,我们插入 suspend_enable 和 suspend_disable来标志安全区域的界限。
【1】 ORP (Open Runtime Platform), http://orp.sf.net ;
【译文】 GC 安全点 和安全区域的更多相关文章
- Java 内存区域和GC机制分析
目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...
- Java 内存区域和GC机制
目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...
- Java系列笔记(3) - Java 内存区域和GC机制
目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...
- 【转】Java之 内存区域和GC机制
转自:Leo Chin 目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage ...
- Java 内存区域和GC机制--备用
Java垃圾回收概况 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代 ...
- JAVA内存区域和GC机制
目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...
- Java内存区域和GC机制篇
Java内存区域和GC机制一.目录 1.Java垃圾回收概括 2.Java内存区域 3.Java对象的访问方式 4.Java内存访问机制 5.Java GC 机制 6.Java垃圾收集器 二.Java ...
- 【转载】Java系列笔记(3) - Java 内存区域和GC机制
Java系列笔记(3) - Java 内存区域和GC机制 转载:原文地址http://www.cnblogs.com/zhguang/p/3257367.html 目录 Java垃圾回收概况 Java ...
- 转载:Java 内存区域和GC机制
原文链接:http://www.cnblogs.com/hnrainll/archive/2013/11/06/3410042.html 目录 Java垃圾回收概况 Java内存区域 Java对象的访 ...
随机推荐
- Cmake的交叉编译
http://www.cmake.org/Wiki/CMake_Cross_Compiling
- Android项目部署时,发生AndroidRuntime:android.view.InflateException: Binary XML file line #168: Error inflating class错误
这个错误也是让我纠结了一天,当时写的项目在安卓虚拟机上运行都很正常,于是当我部署到安卓手机上时,点击登陆按钮跳转到用户主界面的时候直接结束运行返回登陆界面. 当时,我仔细检查了一下自己的代码,并 ...
- Microsoft Azure Point to Site VPN替代方案
Microsoft Azure提供了Point to Site VPN,但有时候这并不能满足我们的需求,例如:Point to Site VPN是SSTP VPN,只能支持Window客户端拨入,而且 ...
- 转: 深入理解Linux修改hostname
from: http://www.cnblogs.com/kerrycode/p/3595724.html 写的相当详细!!! 深入理解Linux修改hostname 2014-03-12 10:17 ...
- 字符加密Cipher(bzoj 1031)
Description 喜欢钻研问题的JS同学,最近又迷上了对加密方法的思考.一天,他突然想出了一种他认为是终极的加密办法 :把需要加密的信息排成一圈,显然,它们有很多种不同的读法.例如下图,可以读作 ...
- iptables能够做什么
前言 在这两篇博文中已经具体分析了iptable的原理和在openwrt里面的实例 http://www.cnblogs.com/tanhangbo/p/4550455.html http://www ...
- Tarjan
//求强连通分量 void uni(int x,int y){ if (rank[x]<rank[y]){ fa[x]=y; size[y]+=size[x]; }else{ rank[x]+= ...
- JavaScript模板引擎artTemplate.js——如何引入模板引擎?
artTeamplate.js在github上的地址:artTemplate性能卓越的js模板引擎 引入模板引擎,就是引入外部javascript啦,并且artTemplate.js不依赖其他第三方库 ...
- Java学习笔记-多线程-创建线程的方式
创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...
- CSS编写技巧
1.尽量少的使用全局的重置代码 全局重置代码:*{margin:0; padding:0;}对于熟悉CSS的人来说并不陌生,并且有很多人的第一句CSS代码就是这句.它可以避免不同浏览器的默认间距不同而 ...