Safepoints: Meaning, Side Effects and Overheads(译文)

Safepoints: Meaning, Side Effects and Overheads (安全点:含义、副作用和开销)

  去年,我一直在进行有关profiling以及JVM运行时/执行的一些讨论,在这两者中,我都发现自己遇到了安全点这一话题。 大多数人对安全点的存在一无所知,并且在一个人满为患的房间 我通常仅能找到1或2个对该术语不陌生的开发人员。 这并不奇怪,因为安全点不是Java语言规范的一部分。 但是,由于安全点是每个JVM实现(我知道)的一部分,并且起着重要作用,因此,我在此进行了一些小小的尝试对它进行概述分析。

什么是安全点?

  最近有人问我:“安全点就像安全的词吗?” 简短的答案是“不是真的”,但是由于我喜欢这个比喻,因此我将其与之对应:想象一下,如果您将有一个充满了所有线程的JVM,这些线程都忙,费力,正在改变堆。 其中一些具有<gasp>共享的可变状态。 他们像动物一样,同时改变彼此的状态。有些人站在角落改变自己的状态(他们会失明)。突然,一个霓虹灯闪烁着(菠萝)PINEAPPLES一词。变异者一个接一个地停止了它们猖獗的 堆 堆放,等着汗水淋漓。当最后一个变种人停下来时,一群精灵进来,倒空烟灰缸,装满所有饮料,拖走水坑,然后尽快消失到北极。 该标志已关闭,线程返回到它。<内部对话>太多了吗? 这里的阴影就像35c,所以出汗就像...不? 没关系</ 内部对话>

  在网上可以找到很多关于Safepoints的参考,这里是我对更细致的使用/定义的尝试,从现在开始,没有汗水的变异者。

  1. 安全点是对执行线程的状态有充分描述的执行范围(A safepoint is a range of execution where the state of the executing thread is well described. )。 变异器线程是操纵JVM堆的线程(您所有的Java线程都是变异器。非Java线程在调用与堆进行交互的JVM API时也可以被视为变异器)。
  2. 在安全点上,变异器线程在与堆的交互中处于已知且定义明确的位置。这意味着堆栈上的所有引用都已映射(在已知位置),并且JVM可以account for所有引用。只要线程保持在安全点,我们就可以安全地操作堆+堆栈,以使线程在离开安全点时对世界的看法保持一致。

  当JVM由于GC或许多其他原因之一而希望检查或更改堆时,这特别有用。如果无法解释堆栈上的引用,并且JVM要运行GC,则可能会遗漏某些对象仍处于活动状态(从堆栈中引用)并收集它们的事实,或者它可能移动某些对象而不更新堆栈上的引用 导致内存损坏。

  这是部分活动列表,只有所有变种线程都处于安全点时,JVM才运行这些活动,并且只有在释放后才能离开(在全局安全点处),这些活动有时称为安全点操作

  • Some GC phases (the Stop The World kind) (phases--阶段)
  • JVMTI stack sampling methods (not always a global safepoint operation for Zing))
  • Class redefinition
  • Heap dumping
  • Monitor deflation (not a global safepoint operation for Zing)
  • Lock unbiasing
  • Method deoptimization (not always)
  • And many more!

  Azul自己的JavaOne 2014的John Cuthbertson的一篇精彩演讲谈到了安全点的背景知识,并分配了有关GC以外的安全点操作的详细信息(我们在Azul认为GC是一个已解决的问题,所以谈话进入了停止的其余原因)。

  请注意,仅在某些JVM实现中才存在请求全局安全点和线程安全点之间的区别(例如Zing,Azul Systems JVM。提醒:我为Azul工作)。 重要的是,它在OpenJDK / Oracle JVM上不存在。 这意味着Zing可以将单个线程带到安全点。

总结一下:

  • 安全点是常见的JVM实现细节
  • 它们用于使变异线程处于暂停状态,而JVM则“修复了东西”
  • 在OpenJDK / Oracle上,每个安全点操作都需要一个全局安全点
  • 当前所有的JVM对全局安全点都有一些要求

我的线程什么时候处于安全点?

  因此,将线程置于安全点可以使JVM继续进行托管运行时魔术表演,太棒了!这种古怪的状态何时发生?

  • 如果Java线程在锁或同步块上阻塞,在监视器上等待,驻留或在阻塞IO上阻塞,则它在安全点上。本质上,所有这些都可以作为Java线程的有序调度事件,也可以作为在线程被置于安全点之前搁置的整理工作的一部分。
  • 执行JNI代码时,Java线程处于安全点。 在越过本机调用边界之前,将堆栈保持一致状态,然后再移交给本机代码。 这意味着线程仍可以在安全点运行。
  • 正在执行字节码的Java线程不在安全点上(或至少JVM无法假定它在安全点上)。
  • 在调度之前,未在安全点中断(由操作系统)的Java线程不会被带到安全点。

JVM和正在运行的Java线程在安全点之间具有以下关系:

  • JVM无法强制任何线程进入安全点状态。
  • JVM可以阻止线程离开安全点状态。

 那么,JVM如何使所有线程进入安全点状态? 问题在于将线程挂起在已知状态,而不仅仅是中断它。为了实现此目标,JVM使Java线程在观察到“安全点标志”的情况下将其自身挂在方便的位置。

 将Java线程带到安全点

  Java线程以“合理”的时间间隔轮询“安全点标志”(全局或线程级别),并在观察到“转到安全点”标志时转换为安全点状态(线程在安全点被阻塞)。这很简单,但是由于我们不想花费所有时间检查是否需要停止C1 / C2编译器(-client / -server JIT编译器),因此请尽量减少安全点轮询。除了标志检查本身的成本之外,保持“已知状态”会为某些优化的实现增加极大的复杂性,因此,将安全点保持更远的距离将为优化提供更大的范围。这些考虑因素共同导致安全点民意调查的位置如下:

  • 在解释器中运行时,在任意两个字节码之间(有效)
  • 在C1 / C2编译代码中的“非计数”循环后沿 ( On 'non-counted' loop back edge in C1/C2 compiled code )
    • A common type of program loop is one that is controlled by an integer that counts up from a initial value to an upper limit. Such a loop is called a counting loop. The integer is called a loop control variable. Loops are implemented with the conditional branch, jump, and conditional set instructions.
  • C1 / C2编译代码中的方法进入/退出(Zing进入,OpenJDK退出)。 请注意,内联方法时,编译器将删除这些安全点轮询。

 ------------------------------------------------------------------------------------------------------------

补充JIT:

  JIT 是 just in time 的缩写, 也就是即时编译编译器。使用即时编译器技术,能够加速 Java 程序的执行速度。下面,就对该编译器技术做个简单的讲解。

  当 JVM 执行代码时,它并不立即开始编译代码。这主要有两个原因:

  首先,如果这段代码本身在将来只会被执行一次,那么从本质上看,编译就是在浪费精力。因为将代码翻译成 java 字节码相对于编译(编译成二进制)这段代码并执行代码来说,要快很多。

  当然,如果一段代码频繁的调用方法,或是一个循环,也就是这段代码被多次执行,那么编译就非常值得了。因此,编译器具有的这种权衡能力首先执行解释后的代码然后再去分辨哪些方法会被频繁调用来保证其本身的编译。其实说简单点,就是 JIT 在起作用,我们知道,对于 Java 代码,刚开始都是被编译器编译成字节码文件,然后字节码文件会被交由 JVM 解释执行,所以可以说 Java 本身是一种半编译半解释执行的语言。Hot Spot VM 采用了 JIT compile 技术,将运行频率很高的字节码直接编译为机器指令执行以提高性能,所以当字节码被 JIT 编译为机器码的时候,要说它是编译执行的也可以。也就是说,运行时,部分代码可能由 JIT 翻译为目标机器指令(以 method 为翻译单位,还会保存起来,第二次执行就不用翻译了)直接执行。

   JIT 编译器在运行程序时有两种编译模式可以选择,并且其会在运行时决定使用哪一种以达到最优性能。这两种编译模式的命名源自于命令行参数(eg: -client 或者 -server)。JVM Server 模式与 client 模式启动,最主要的差别在于:-server 模式启动时,速度较慢,但是一旦运行起来后,性能将会有很大的提升。原因是:当虚拟机运行在-client 模式的时候,使用的是一个代号为 C1 的轻量级编译器,而-server 模式启动的虚拟机采用相对重量级代号为 C2 的编译器。C2 比 C1 编译器编译的相对彻底,服务起来之后,性能更高。

 ------------------------------------------------------------------------------------------------------------

原文地址:https://www.cnblogs.com/wxdlut/p/14154103.html