JMH使用javaagent进行测试

JMH使用javaagent进行测试,java,performance-testing,jmh,btrace,Java,Performance Testing,Jmh,Btrace,我试图测量JVM代理对性能的影响,以确保它不会使我们尝试运行的测试无效(并且可能有理由从prod中获取一些样本)。本例是一组BTrace脚本,将在自动负载测试期间运行,但该问题可能对任何代理都是通用的 为了运行基准测试,我建立了一个小型JMH项目,并将代理附加为: java -javaagent:/home/ssube/btrace/build/btrace-agent.jar=scriptdir=/home/ssube/btrace/scripts/,port=0 -jar benchmark

我试图测量JVM代理对性能的影响,以确保它不会使我们尝试运行的测试无效(并且可能有理由从prod中获取一些样本)。本例是一组BTrace脚本,将在自动负载测试期间运行,但该问题可能对任何代理都是通用的

为了运行基准测试,我建立了一个小型JMH项目,并将代理附加为:

java -javaagent:/home/ssube/btrace/build/btrace-agent.jar=scriptdir=/home/ssube/btrace/scripts/,port=0 -jar benchmarks.jar
这样做会导致每次JMH分叉JVM时出现以下错误:

# Run progress: 0.00% complete, ETA 00:02:00
# Fork: 1 of 1
Exception in thread "main" java.lang.IllegalArgumentException: org.openjdk.jmh.runner.options.CommandLineOptions; local class incompatible: stream classdesc serialVersionUID = 8906142321598115825, local class serialVersionUID = 7529911323947566771
    at org.openjdk.jmh.runner.ForkedMain.main(ForkedMain.java:72)
<binary link had failed, forked VM corrupted the stream? Use EXTRA verbose to print exception>
<forked VM failed with exit code 1>
<stdout last='10 lines'>
</stdout>
<stderr last='10 lines'>
Exception in thread "main" java.lang.IllegalArgumentException: org.openjdk.jmh.runner.options.CommandLineOptions; local class incompatible: stream classdesc serialVersionUID = 8906142321598115825, local class serialVersionUID = 7529911323947566771
    at org.openjdk.jmh.runner.ForkedMain.main(ForkedMain.java:72)
</stderr>

# VM invoker: /usr/java/jdk1.8.0_11/jre/bin/java
# VM options: -javaagent:/home/ssube/btrace/build/btrace-agent.jar=scriptdir=/home/ssube/btrace/scripts/,port=0
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.stackoverflow.questions.ShaderBench.testProcessProc
#运行进度:完成0.00%,预计时间00:02:00
#叉子:1/1
线程“main”java.lang.IllegalArgumentException中的异常:org.openjdk.jmh.runner.options.CommandLineOptions;本地类不兼容:流classdesc serialVersionUID=8906142321598115825,本地类serialVersionUID=75299112394756771
位于org.openjdk.jmh.runner.ForkedMain.main(ForkedMain.java:72)
线程“main”java.lang.IllegalArgumentException中的异常:org.openjdk.jmh.runner.options.CommandLineOptions;本地类不兼容:流classdesc serialVersionUID=8906142321598115825,本地类serialVersionUID=75299112394756771
位于org.openjdk.jmh.runner.ForkedMain.main(ForkedMain.java:72)
#VM调用程序:/usr/java/jdk1.8.0_11/jre/bin/java
#VM选项:-javaagent:/home/ssube/btrace/build/btrace-agent.jar=scriptdir=/home/ssube/btrace/scripts/,端口=0
#预热:20次迭代,每次1s
#测量:20次迭代,每次1s
#线程:1个线程,将同步迭代
#基准模式:吞吐量,操作/时间
#基准:com.stackoverflow.questions.ShaderBench.testProcessProc
我的所有类都不是可序列化的,也没有serialVersionUID。JMH基准测试在没有连接BTrace代理的情况下工作,代理和脚本在没有JMH的情况下处理代码


(如何)将javaagent附加到一组JMH基准测试并捕获由该代理引起的性能差异?

我最近遇到了同样的问题。我认为这是有关的。从基准测试的输出中可以看出,JMH创建了一个fork
1/1
,其中JMH为每个测试行启动一个新的JVM实例。这些JVM不再连接您的代理

这会影响使用代理重新定义的类,这些类在分叉JVM上加载时没有代理。因此,您会遇到序列化问题。由于没有为类定义显式UID,这些UID是隐式计算的。因此,分叉JVM将认识到原始JVM的类与分叉JVM的类不同,从而导致错误。您可以通过显式定义UID来避免这些序列化问题,但是您的类仍然不会被检测

相反,试着用
@Fork(0)
注释基准测试,这会完全禁用Fork。但是,如果这太扭曲了你的结果,你应该小心。在这种情况下,可以通过分析对代码进行大量优化。如果您在基准测试之间共享代码,而来自第一个基准测试的概要文件将影响其他基准测试,通常会更糟,那么这一点尤其正确

另一个解决方案是将代理应用于分叉的JVM。为此,
@Fork
注释提供了几个参数。例如,您可以定义:

@Fork(jvmArgs = "-javaagent:/home/ssube/btrace/build/btrace-agent.jar=scriptdir=/home/ssube/btrace/scripts/,port=0")

但是请注意,这使您的构建依赖于您的文件系统。

JMH maintainer。从维护人员的角度来看,它感觉像是JMH的错误,因为它没有为自己的内部类提供SUID,所以它可以抵抗良性的检测。现在已通过以下方式修复此问题:

请尝试使用,看看它是否为您解决了问题。请注意,提供suid并不能保护我们免受Java代理可以进行的序列化形式的公然更改(例如,注入非瞬态字段),因此如果BTrace的行为(错误)是这样的,我们就有麻烦了

如果上面的JMH更改不起作用,请发送一些最小的可复制场景给,我们将看到可以采取哪些措施来缓解该问题


否则,请查找是否从检测中排除
org.openjdk.jmh.
类。

@Fork(jvmArgs)
方法对我不起作用:出现相同的serialVersionUID错误。使用
@Fork(0)
确实有效,幸运的是不会对我的测试结果造成太大的改变,但这确实是一个很好的通用解决方案。@ssube我刚刚看到您在JMH类中遇到了一个序列化错误。使用最近的JMH修复程序,它应该可以在不向类添加ID的情况下工作。有关详细信息,请参见Alekseys的答案。目前这是一个功能完善的解决方案。根据您的测试,分叉可能会改变结果,也可能不会改变结果(在我的情况下它不会)。但是,我将接受一个修复的答案,而不是解决方法。很难击败一个维护者修复的答案,您可以原谅。今天就来看看,谢谢。我会让你知道我发现了什么。这似乎成功了。测试时无需在
@Fork
注释中指定任何JVMARG,具有各种Fork计数;现在似乎一切正常。请注意,BTrace确实需要指定
port=0
,或者每个fork都尝试绑定到默认值,但失败。BTrace不会注入非临时字段,因为JVM重传类的当前实现不支持引入新字段。@J.B:很好,那么我们就安全了。
changeset:   960:3d44d68e45be
tag:         tip
user:        shade
date:        Sat Aug 16 15:00:18 2014 +0400
summary:     Apply SUIDs for all Serializable classes: this protects from the benign instrumentation.