JMH使用javaagent进行测试

时间:2014-08-15 14:45:48

标签: 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 benchmarks.jar

这样做会导致每次JM分叉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

我的所有类都不可序列化或具有serialVersionUID。 JMH基准测试在没有附加BTrace代理的情况下工作,代理和脚本在没有JMH的情况下处理代码。

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

2 个答案:

答案 0 :(得分:3)

JMH维护者在这里。从维护者的角度来看,感觉就像JMH错误一样,没有为自己的内部类提供SUID,因此它可以抵抗良性仪器。现在使用3d44d68e45be

解决了这个问题
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.

请尝试使用bleeding edge JMH,看看它是否能为您解决问题。请注意,提供SUID并不能保护我们免受Java代理可以进行的序列化形式的明显更改(例如,注入非瞬态字段),因此如果BTrace(错误)表现得那样,我们就会遇到麻烦。

如果上面的JMH更改不起作用,请将一些最小的可重现方案发送到jmh-dev mailing list,我们会看到可以采取哪些措施来缓解此问题。

否则,请查看从检测中排除org.openjdk.jmh.*个类。

答案 1 :(得分:1)

我最近遇到了同样的问题。我假设它与JMH's forking有关。正如您可以从基准测试的输出中读取的那样,JMH创建了一个fork 1 of 1,其中JMH为每个测试行启动一个 fresh JVM实例。这些JVM不再附加您的代理。

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

相反,尝试用@Fork(0)注释基准来完全禁用分叉。但是,如果这会过度扭曲您的结果,您应该小心。当您的代码可以通过分析进行大量优化时就是这种情况。如果您在基准测试之间共享代码,尤其如此,那么第一个基准测试中的配置文件将影响您的其他基准测试,通常会更糟糕。

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

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

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