JavaGC:什么编程风格使安全点的实现更容易(更快)?

JavaGC:什么编程风格使安全点的实现更容易(更快)?,java,garbage-collection,Java,Garbage Collection,假设我需要垃圾收集器的帮助。 在GC中花费较少时间的方法之一是更快地到达安全点 在启动GC之前,所有线程都必须停在安全点 Safepoint是执行流中的一个“特殊位置”,在这里可以安全地停止线程以执行GC 我想尽量减少代码中安全点的等待时间。 什么样的编码风格可以帮助我更快地到达安全点 什么样的风格让这个任务更不舒服 在“风格”一词中,我指的是语言中帮助(或阻止)快速“获取”safepoint的一切 示例: 磁盘IO工作是否会使问题更加严重 JNI是否让它变得更麻烦 Thread.sleep()

假设我需要垃圾收集器的帮助。 在GC中花费较少时间的方法之一是更快地到达安全点

在启动GC之前,所有线程都必须停在安全点

Safepoint是执行流中的一个“特殊位置”,在这里可以安全地停止线程以执行GC

我想尽量减少代码中安全点的等待时间。 什么样的编码风格可以帮助我更快地到达安全点

什么样的风格让这个任务更不舒服

在“风格”一词中,我指的是语言中帮助(或阻止)快速“获取”safepoint的一切

示例:

磁盘IO工作是否会使问题更加严重

JNI是否让它变得更麻烦

Thread.sleep()会让它更难吗

同步(…)会让事情变得更难吗

等等


简而言之,哪些java操作和方法会使快速“获取”safepoint成为问题?还有什么帮助呢?

让我们从以下注释开始(来自OpenJDK源代码中的“src/hotspot/share/runtime/safepoint.cpp”文件):

由此,我得出以下结论:

  • 当JVM线程解释字节码时,它可以在任何字节码之后到达安全点
  • 当JVM线程运行已经JIT编译的代码时,行为取决于JIT编译何时/何地注入指令以轮询“Safepoint轮询”页面
  • 当本机(JNI)线程正在运行时,从安全点的角度来看,它会被忽略。但是,我相信如果safepoint正在进行,一些JNI调用将导致本机线程阻塞
  • 如果Java线程被阻塞(例如,被线程调度程序或I/O系统调用阻塞),或者如果它改变了状态,则safepoint可以继续
但这只是部分地回答了这个问题。问题在于JIT编译代码中的安全点轮询是由JIT编译器添加的指令完成的。控制这一点的逻辑未指定1,可能依赖于实现。此外,我听说使用
System.arraycopy
和其他内置/固有操作进行内存复制可能需要很长时间,而无需安全点检查

关于你的具体例子:

磁盘IO工作是否会使问题更加严重

没有

JNI是否让它变得更麻烦

没有

Thread.sleep()会让它更难吗

没有

同步(…)会让事情变得更难吗

没有

关于你的一般性问题:

什么样的方式让这项[到达安全点]的任务更不舒服

避免长计数循环



1-根据,轮询插入到非计数循环中的向后分支上以及方法入口或出口上。这取决于平台。

嘿,解开我的问题。您是否确实观察到GC有任何需要修复的问题?避免过早的优化等等。我不是在问如何解决问题。没有问题。我问的是如何编程(编写代码、设计),以便让GC在等待safepoint方面花费更少的时间。好吧,换言之,是什么让您认为这在实践中是必要的?这个问题实际上与GC无关(它需要safepoints,但也需要类加载或(AFAIK)去优化)。JVM中存在(UDE是什么?)一些bug,使得到达保存点花费的时间太长。这也是在频繁检查中浪费时间和在只有一个线程等待最后一个线程到达保存点的情况下浪费时间之间的折衷。这可能有JVM选项,但我怀疑您是否应该/能够通过您的编码风格做任何事情。
  // Java threads can be in several different states and are
  // stopped by different mechanisms:
  //
  //  1. Running interpreted
  //     The interpreter dispatch table is changed to force it to
  //     check for a safepoint condition between bytecodes.
  //  2. Running in native code
  //     When returning from the native code, a Java thread must check
  //     the safepoint _state to see if we must block.  If the
  //     VM thread sees a Java thread in native, it does
  //     not wait for this thread to block.  The order of the memory
  //     writes and reads of both the safepoint state and the Java
  //     threads state is critical.  In order to guarantee that the
  //     memory writes are serialized with respect to each other,
  //     the VM thread issues a memory barrier instruction
  //     (on MP systems).  In order to avoid the overhead of issuing
  //     a memory barrier for each Java thread making native calls, each Java
  //     thread performs a write to a single memory page after changing
  //     the thread state.  The VM thread performs a sequence of
  //     mprotect OS calls which forces all previous writes from all
  //     Java threads to be serialized.  This is done in the
  //     os::serialize_thread_states() call.  This has proven to be
  //     much more efficient than executing a membar instruction
  //     on every call to native code.
  //  3. Running compiled Code
  //     Compiled code reads a global (Safepoint Polling) page that
  //     is set to fault if we are trying to get to a safepoint.
  //  4. Blocked
  //     A thread which is blocked will not be allowed to return from the
  //     block condition until the safepoint operation is complete.
  //  5. In VM or Transitioning between states
  //     If a Java thread is currently running in the VM or transitioning
  //     between states, the safepointing code will wait for the thread to
  //     block itself when it attempts transitions to a new state.
  //