当前位置: 首页 > 面试题库 >

newInstance vs new jdk-9 / jdk-8和jmh

黄靖
2023-03-14
问题内容

我在这里看到了很多线程,它们可以进行比较并尝试回答哪个更快:newInstancenew operator

查看源代码,似乎newInstance应该 慢很多
,我的意思是它进行了许多安全检查并使用了反射。我决定进行测量,首先运行jdk-8。这是使用的代码jmh

@BenchmarkMode(value = { Mode.AverageTime, Mode.SingleShotTime })
@Warmup(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)   
@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)    
@State(Scope.Benchmark) 
public class TestNewObject {
    public static void main(String[] args) throws RunnerException {

        Options opt = new OptionsBuilder().include(TestNewObject.class.getSimpleName()).build();
        new Runner(opt).run();
    }

    @Fork(1)
    @Benchmark
    public Something newOperator() {
       return new Something();
    }

    @SuppressWarnings("deprecation")
    @Fork(1)
    @Benchmark
    public Something newInstance() throws InstantiationException, IllegalAccessException {
         return Something.class.newInstance();
    }

    static class Something {

    } 
}

我认为这里没有什么大的惊喜(JIT进行了很多优化,使这种差异不 那么大 ):

Benchmark                  Mode  Cnt      Score      Error  Units
TestNewObject.newInstance  avgt    5      7.762 ±    0.745  ns/op
TestNewObject.newOperator  avgt    5      4.714 ±    1.480  ns/op
TestNewObject.newInstance    ss    5  10666.200 ± 4261.855  ns/op
TestNewObject.newOperator    ss    5   1522.800 ± 2558.524  ns/op

热代码的差异约为 2 倍,单次射击时间的差异更糟。

现在,我切换到jdk-9(以防万一,请构建157)并运行相同的代码。结果:

 Benchmark                  Mode  Cnt      Score      Error  Units
 TestNewObject.newInstance  avgt    5    314.307 ±   55.054  ns/op
 TestNewObject.newOperator  avgt    5      4.602 ±    1.084  ns/op
 TestNewObject.newInstance    ss    5  10798.400 ± 5090.458  ns/op
 TestNewObject.newOperator    ss    5   3269.800 ± 4545.827  ns/op

那是热码的 50倍之 差。我正在使用最新的jmh版本(1.19.SNAPSHOT)。

在将另一种方法添加到测试后:

@Fork(1)
@Benchmark
public Something newInstanceJDK9() throws Exception {
    return Something.class.getDeclaredConstructor().newInstance();
}

以下是jdk-9的总体结果:

TestNewObject.newInstance      avgt    5    308.342 ±   107.563  ns/op
TestNewObject.newInstanceJDK9  avgt    5     50.659 ±     7.964  ns/op
TestNewObject.newOperator      avgt    5      4.554 ±     0.616  ns/op

有人可以阐明 为什么会有如此大的差异 吗?


问题答案:

首先,问题与模块系统无关(直接)。

我注意到,即使使用JDK 9,其第一个预热迭代newInstance也与使用JDK 8一样快。

# Fork: 1 of 1
# Warmup Iteration   1: 10,578 ns/op    <-- Fast!
# Warmup Iteration   2: 246,426 ns/op
# Warmup Iteration   3: 242,347 ns/op

这意味着JIT编译中出现了问题。
-XX:+PrintCompilation确认基准是在第一次迭代后重新编译的:

10,762 ns/op
# Warmup Iteration   2:    1541  689   !   3       java.lang.Class::newInstance (160 bytes)   made not entrant
   1548  692 %     4       bench.generated.NewInstance_newInstance_jmhTest::newInstance_avgt_jmhStub @ 13 (56 bytes)
   1552  693       4       bench.generated.NewInstance_newInstance_jmhTest::newInstance_avgt_jmhStub (56 bytes)
   1555  662       3       bench.generated.NewInstance_newInstance_jmhTest::newInstance_avgt_jmhStub (56 bytes)   made not entrant
248,023 ns/op

然后-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining指出内联问题:

1577  667 %     4       bench.generated.NewInstance_newInstance_jmhTest::newInstance_avgt_jmhStub @ 13 (56 bytes)
                           @ 17   bench.NewInstance::newInstance (6 bytes)   inline (hot)
            !                @ 2   java.lang.Class::newInstance (160 bytes)   already compiled into a big method

消息“已经编译为大方法”
表示编译器无法内联Class.newInstance调用,因为被调用方的已编译大小大于InlineSmallCode值(默认为2000)。

当我使用来重新运行基准测试时-XX:InlineSmallCode=2500,它又变得很快了。

Benchmark                Mode  Cnt  Score   Error  Units
NewInstance.newInstance  avgt    5  8,847 ± 0,080  ns/op
NewInstance.operatorNew  avgt    5  5,042 ± 0,177  ns/op

您知道,JDK 9现在将 G1作为默认GC 。如果我退回到Parallel
GC,即使使用default,基准测试也会很快InlineSmallCode

使用以下命令重新运行JDK 9基准测试-XX:+UseParallelGC

Benchmark                Mode  Cnt  Score   Error  Units
NewInstance.newInstance  avgt    5  8,728 ± 0,143  ns/op
NewInstance.operatorNew  avgt    5  4,822 ± 0,096  ns/op

G1要求每当对象存储发生时就设置一些障碍,这就是为什么编译后的代码会变得更大一些,从而Class.newInstance超过默认InlineSmallCode限制。编译Class.newInstance变得更大的另一个原因是,反射代码已在JDK
9中进行了稍微的重写。

TL; DR
JIT无法内联Class.newInstance,因为InlineSmallCode已超出限制。Class.newInstance由于JDK
9中反射代码的更改以及默认GC已更改为G1,因此的编译版本变得更大。



 类似资料:
  • 以下代码在使用JDK 8和JDK 9运行时有不同的结果。 在JDK 8(版本1.8.0_172)下,代码打印: 但是使用JDK 9(版本9.0.1),代码返回: 我已经检查了两个JDK版本,它们是正确的。为什么代码会产生不同的结果?我的程序有什么问题吗?

  • 毕昇 JDK 是华为内部 OpenJDK 定制版 Huawei JDK 的开源版本,是一个高性能、可用于生产环境的 OpenJDK 发行版。 Huawei JDK 运行在华为内部 500 多个产品上,积累了大量使用场景和 java 开发者反馈的问题和诉求,解决了业务实际运行中遇到的多个问题,并在 ARM 架构上进行了性能优化,毕昇 JDK 运行在大数据等场景下可以获得更好的性能。 毕昇 JDK 8

  • 我在Mac OS上安装了JDK 9的发布版本。 jshell工作得很好,Jigsee模块支持工作,但没有jlink: 这是空的: 仅供参考:

  • 我对netbeans的hdpi有一个问题,当我打开它时,图标变得太小。然后我发现了下面的问题。有人说jdk9解决了分辨率问题。 https://netbeans.org/bugzilla/show_bug.cgi?id=252452 这里是我的环境: 操作系统:windows 10(64位) JDK:JDK9 BUILD149 netbeans 8.2 我下载了jdk9 在我输入cmd后<代码>n

  • 我有一个Hibernate 5项目,它完美地构建和运行在Java 8上。我尝试在JDK 9 ea build 171上测试它。由于它是一个庞大的项目,并且有其他依赖项,我不得不添加<code>Java.xml。将模块绑定到JVM配置以进行测试: 如果使用聚合模块java.se,我还可以解决其他问题。ee(推荐): 我得到一个例外: 我甚至不明白为什么会发生这种情况,因为JTA库(带有类)在测试运行

  • 问题内容: 我正在学习Java 8 lambda表达式,我想问一下以下与我遇到的函数接口中的方法有关的Java代码。 在IDE上执行程序时,它不输出。我期望它会给。 问题答案: 我假设您正在Java 9下运行它?您没有更改流的属性,因此不需要执行任何一个或全部执行。 换句话说,您关心的只是最终结果,但与此同时,您不以 任何 方式(例如通过或)更改的初始大小。这是在Streams中完成的优化。 顺便