为什么Java 8的Arraylist.add()比Java 17更快?

问题描述 投票:0回答:1

当我在大循环中使用 Arraylist.add() 方法时,Java 8 比 Java 17 更快。

这就是为什么?这是因为方法

calculateCapacity(Object[] elementData, int minCapacity)
吗?

这是我的代码:

public class T2 {
    static Map<String, Object> map = new HashMap<>();

    static {
        map.put("key", "value");
    }

    // loop for 10000000 times
    public static long TIMES = 10000000;

    /**
     * run with jdk17 and jdk8
     * @param args
     */
    public static void main(String[] args) {
        long l1 = 0;
        int ii = 10;
        for (int i = 0; i < ii; i++) {
            StopWatch s = new StopWatch();
            s.start();
            t3();
            s.stop();
            l1 += s.getTotalTimeMillis();
            System.out.println(s.getTotalTimeMillis());
            System.out.println(s.prettyPrint());
        }
        System.out.println("loop" + ii + "times");
        System.out.println("Average time :" + l1 / ii+"ms");
    }

    static void t3() {
        Object o = map.get("key");
        List<String> s = new ArrayList<>();
        for (int i = 0; i < TIMES; i++) {
            s.add(o.toString());
        }
    }
}

我已在 Windows 11 和 macOS 上运行此程序,获得了相同的结果:Java 8 比 Java 17 更快。

java java-8 java-17
1个回答
0
投票

性能可能会因 JVM 优化、垃圾收集和底层数据结构等各种因素而有所不同。然而,在 Java 8 和 Java 17 中,

ArrayList.add()
方法基本上是相似的。

一个具体的区别是您提到的

calculateCapacity
方法。在 Java 17 中,
ArrayList
使用
ArraysSupport.newLength
进行更强大的溢出检查,这可能会增加一些开销。然而,对于大多数用例来说,这种开销通常可以忽略不计。

如果您始终观察到显着差异,请确保:

  1. 两个 JDK 均已正确配置。
  2. 基准测试是使用 JMH 等适当的工具完成的,而不仅仅是简单的循环计时。
  3. 您正在比较两个 Java 版本的“HotSpot”与“HotSpot”或“OpenJ9”与“OpenJ9”。

总而言之,虽然实现上可能存在细微差别,但对于大多数应用程序来说,它们不应导致显着的性能差异。正确的基准测试是准确绩效评估的关键。

您当前的基准测试方法有一些局限性:

  1. 预热:JVM 需要预热时间来进行即时 (JIT) 编译。您的代码没有考虑到这一点。
  2. 垃圾收集:您没有控制 GC,这可能会扭曲结果。
  3. 微基准测试:您正在使用简单的循环来测量性能,这对于 Java 中的微基准测试来说并不理想。

更好的方法是使用 Java Microbenchmarking Harness (JMH)。它是一个专为性能基准测试而设计的 Java 库,它控制许多可能影响性能测量的变量。

以下是使用 JMH 时您的代码的外观:

import org.openjdk.jmh.annotations.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class MyBenchmark {

    static Map<String, Object> map = new HashMap<>();
    static {
        map.put("key", "value");
    }
    static final long TIMES = 10000000;
    Object o = map.get("key");

    @Benchmark
    public void testArrayListAdd() {
        List<String> s = new ArrayList<>();
        for (int i = 0; i < TIMES; i++) {
            s.add(o.toString());
        }
    }
}

要运行此程序,您需要将 JMH 作为依赖项包含在内,然后执行基准测试。

这可以让您更准确地衡量不同版本的 Java 之间的性能差异。

© www.soinside.com 2019 - 2024. All rights reserved.