3

我正在尝试衡量 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

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

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

4

2 回答 2

6

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.

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

如果上面的 JMH 更改不起作用,请将一些最小的可重现场景发送到jmh-dev 邮件列表,我们会看看可以做些什么来缓解这个问题。

否则,寻找org.openjdk.jmh.*从检测中排除类。

于 2014-08-16T11:01:52.847 回答
1

我最近遇到了同样的问题。我认为它与JMH 的分叉有关。正如您可以从基准测试的输出中看到的那样,JMH 创建了一个分支,其中 JMH为每个测试行1 of 1启动一个新的 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")

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

于 2014-08-15T15:14:33.330 回答