什么JVM优化导致这些性能结果?

Seb*_*ian 7 java performance jvm bytecode microbenchmark

执行Java REST服务性能测试我看到一个意外的模式:在每次调用中创建并返回相同值对象的方法比仅返回存储在类或对象字段中的值对象的另一个版本运行得更快.

码:

@POST @Path("inline") public Response inline(String s) { 
    return Response.status(Status.CREATED).build(); 
}    

private static final Response RESP = Response.status(Status.CREATED).build();
@POST @Path("staticfield") public Response static(String s) { 
    return RESP; 
}

private final Response resp = Response.status(Status.CREATED).build();
@POST @Path("field") public Response field(String s) { 
    return resp; 
}
Run Code Online (Sandbox Code Playgroud)

字节代码:

  • 内联(更快):getstatic,invokestatic,invokevirtual,areturn
  • 静态归档(较慢):getstatic,areturn
  • 对象字段(较慢):aload,getfield,areturn

性能(使用Apache AB,单线程,多次运行,结果一致):

  • 内联:17078.29 [#/ sec](平均值)
  • 静电场:5242.64 [#/ sec](平均值)
  • 对象字段:5417.40 [#/ sec](平均值)

环境:RHEL6 + JDK Oracle 1.7.0_60-b19 64位

有可能JVM使用本机代码优化了内联版本,但从未考虑优化其他两个,因为它们已经很小了吗?

Raf*_*ter 4

正如评论中指出的,如果不实际查看程序集,很难判断。由于您正在使用 REST 框架,因此我认为很难从程序集中看出这一点,因为有相当多的代码需要阅读。

相反,我想给您一个有根据的猜测,因为您的代码是应用恒折叠的典型示例。当内联值而不是从字段读取值时,JVM 可以安全地假定该值是常量。因此,当 JIT 编译方法时,常量表达式可以安全地与框架代码合并,这可能会减少 JIT 汇编,从而提高性能。对于字段值,即使是final1,也不能假定为常量值,因为字段值可能会发生变化。(只要字段值不是由javac内联的编译时常量、原语或常量。)因此,JVM 可能无法常量折叠该值。String

您可以在JMH教程中阅读有关常量折叠的更多信息,其中指出:

如果JVM意识到无论如何计算的结果都是一样的,它就可以巧妙地对其进行优化。在我们的例子中,这意味着我们可以将计算移到内部 JMH 循环之外。可以通过始终从状态读取输入、根据该状态计算结果并遵循防止 DCE 的规则来防止这种情况。

我希望你使用这样的框架。否则,您的绩效指标不太可能有效。

通过阅读字节码,您通常无法了解太多有关运行时性能的信息,因为 JIT 编译器可以在优化期间将字节码调整为任何内容。字节代码布局仅在代码被解释时才重要,这通常不是人们衡量性能的状态,因为性能关键的代码始终是 JIT 编译的。