有没有在 Java 9+ 中添加安全点的轻量级方法
Is there a lightweight method which adds a safepoint in Java 9+
在 Java 9+ 中是否有更便宜的方法调用来保持其安全点?
JVM 在运行时删除安全点以提高效率,但这会使分析和监视代码更加困难。出于这个原因,我们特意在 精心选择的 位置添加了琐碎的调用,以确保存在安全点。
public static void safepoint() {
if (IS_JAVA_9_PLUS)
Thread.holdsLock(""); // 100 ns on Java 11
else
Compiler.enable(); // 5 ns on Java 8
}
public static void optionalSafepoint() {
if (SAFEPOINT_ENABLED)
if (IS_JAVA_9_PLUS)
Thread.holdsLock("");
else
Compiler.enable();
}
在 Java 8 上,这种开销很好,但是 Compiler.enable()
在 Java 9+ 中得到了优化,所以我们必须使用更昂贵的方法,或者不启用此功能.
编辑:除了分析器,我还使用 safepoint()
从 Thread.getStackTrace()
中获取更好的细节,以便应用程序可以自我分析,例如当执行某项操作花费的时间太长时。
在 HotSpot JVM 中,安全点(JVM 可以安全停止 Java 线程的位置)是
- 在来自非内联方法的 return 之前;
- 在向后分支(即在循环中),除非循环被计算在内。如果已知迭代次数有限,且循环变量符合整数类型,则对循环进行计数;
- 在线程状态转换时(本机 -> Java,本机 -> VM);
- 在 JVM 运行时的阻塞函数中。
以上所有地方,除了向后的分支,都意味着至少有一个方法调用的开销。所以,显然放置安全点最便宜的方法是编写一个不计数的循环:
public class Safepoint {
private static volatile int one = 1;
public static void force() {
for (int i = 0; i < one; i++) ;
}
}
volatile
保证循环不会被优化器消除,不会被当做计数
我用 -XX:+PrintAssembly
验证了在我调用 Safepoint.force()
的任何地方都插入了安全点轮询指令。调用本身大约需要 1 ns。
但是,由于 JDK8 中的错误,安全点轮询的存在还不能保证从不同线程获得的堆栈跟踪的正确性。本机方法调用设置最后一个 Java 帧锚点,从而“修复”堆栈跟踪。我想这就是您选择本机方法的原因。不过,该错误已在 JDK 9+ 中修复。
顺便说一句,这里有几个比 Thread.holdsLock
开销更低的本机方法:
Thread.currentThread().isAlive()
Runtime.getRuntime().totalMemory()
关于分析,基于安全点的分析器是 completely broken in the first place. This is actually a reason why I started async-profiler 几年前的项目。它的目标是促进对 Java 应用程序的概要分析,开销低且没有安全点偏差。
在 Java 9+ 中是否有更便宜的方法调用来保持其安全点?
JVM 在运行时删除安全点以提高效率,但这会使分析和监视代码更加困难。出于这个原因,我们特意在 精心选择的 位置添加了琐碎的调用,以确保存在安全点。
public static void safepoint() {
if (IS_JAVA_9_PLUS)
Thread.holdsLock(""); // 100 ns on Java 11
else
Compiler.enable(); // 5 ns on Java 8
}
public static void optionalSafepoint() {
if (SAFEPOINT_ENABLED)
if (IS_JAVA_9_PLUS)
Thread.holdsLock("");
else
Compiler.enable();
}
在 Java 8 上,这种开销很好,但是 Compiler.enable()
在 Java 9+ 中得到了优化,所以我们必须使用更昂贵的方法,或者不启用此功能.
编辑:除了分析器,我还使用 safepoint()
从 Thread.getStackTrace()
中获取更好的细节,以便应用程序可以自我分析,例如当执行某项操作花费的时间太长时。
在 HotSpot JVM 中,安全点(JVM 可以安全停止 Java 线程的位置)是
- 在来自非内联方法的 return 之前;
- 在向后分支(即在循环中),除非循环被计算在内。如果已知迭代次数有限,且循环变量符合整数类型,则对循环进行计数;
- 在线程状态转换时(本机 -> Java,本机 -> VM);
- 在 JVM 运行时的阻塞函数中。
以上所有地方,除了向后的分支,都意味着至少有一个方法调用的开销。所以,显然放置安全点最便宜的方法是编写一个不计数的循环:
public class Safepoint {
private static volatile int one = 1;
public static void force() {
for (int i = 0; i < one; i++) ;
}
}
volatile
保证循环不会被优化器消除,不会被当做计数
我用 -XX:+PrintAssembly
验证了在我调用 Safepoint.force()
的任何地方都插入了安全点轮询指令。调用本身大约需要 1 ns。
但是,由于 JDK8 中的错误,安全点轮询的存在还不能保证从不同线程获得的堆栈跟踪的正确性。本机方法调用设置最后一个 Java 帧锚点,从而“修复”堆栈跟踪。我想这就是您选择本机方法的原因。不过,该错误已在 JDK 9+ 中修复。
顺便说一句,这里有几个比 Thread.holdsLock
开销更低的本机方法:
Thread.currentThread().isAlive()
Runtime.getRuntime().totalMemory()
关于分析,基于安全点的分析器是 completely broken in the first place. This is actually a reason why I started async-profiler 几年前的项目。它的目标是促进对 Java 应用程序的概要分析,开销低且没有安全点偏差。