直接 java.nio.ByteBuffer vs Java Array 性能测试

Abi*_*idi 5 java bytebuffer jmh

我想比较直接字节缓冲区(java.nio.ByteBuffer,堆外)和堆缓冲区(通过数组实现)的读取和写入性能。我的理解是,ByteBuffer 在堆外比堆缓冲区至少有两个好处。首先,它不会被 GC 考虑,其次(我希望我做对了)JVM 在读取和写入它时不会使用中间/临时缓冲区。这些优点可能使堆外缓冲区比堆缓冲区更快。如果这是正确的,我不应该期望我的基准显示相同吗?它总是比非堆缓冲区更快地显示堆缓冲区。

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Fork(value = 2, jvmArgs = {"-Xms2G", "-Xmx4G"})
@Warmup(iterations = 3)
@Measurement(iterations = 10)
public class BasicTest {

    @Param({"100000"})
    private int N;

    final int bufferSize = 10000;

    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(8 * bufferSize);
    long buffer[] = new long[bufferSize];


    public static void main(String arep[]) throws  Exception {

        Options opt = new OptionsBuilder()
                .include(BasicTest.class.getSimpleName())
                .forks(1)
                .build();

        new Runner(opt).run();

    }


    @Benchmark
    public void offHeapBuffer(Blackhole blackhole) {

        IntStream.range(0, bufferSize).forEach(index -> {
            byteBuffer.putLong(index, 500 * index);
            blackhole.consume(byteBuffer.get(index));
        });

    }

    @Benchmark
    public void heapBuffer(Blackhole blackhole) {

        IntStream.range(0, bufferSize).forEach(index -> {
            buffer[index] = 500 * index;
            blackhole.consume(buffer[index]);
        });

    }
}
Run Code Online (Sandbox Code Playgroud)

运行完成。总时间:00:00:37

基准 (N) 模式 Cnt 分数误差单位

BasicTest.heapBuffer 100000 avgt 10 0.039 ± 0.003 ms/op

BasicTest.offHeapBuffer 100000 avgt 10 0.050 ± 0.007 ms/op

And*_*eas 5

不会被 GC 考虑

当然GC会考虑。

垃圾收集器确定缓冲区不再使用,然后释放内存。

我不应该期望我的基准测试显示 [该] 堆外缓冲区 [is] 比堆缓冲区快吗?

堆外不会使缓冲区更快地进行内存访问。

一个直接缓冲区会更快,当Java的交流与操作系统的缓冲区的字节数。由于您的代码不执行 I/O,因此使用直接缓冲区没有性能优势。

正如javadoc所说:

给定一个直接字节缓冲区,Java 虚拟机将尽最大努力直接在其上执行本机 I/O 操作。也就是说,它将尝试避免在每次调用底层操作系统的本机 I/O 操作之前(或之后)将缓冲区的内容复制到(或从)中间缓冲区。

  • @Abidi 仅仅因为它在网络上并不意味着它是真的。 (3认同)
  • @abidi 在“DirectByteBuffer”后面分配的内存不是堆的一部分。这意味着 GC 不会扫描它或将其移动到年轻/老年代。这是提高效率的部分原因。(请注意,“DirectByteBuffer”对象本身将像任何其他对象一样被扫描和移动。)作为其“最终确定”的一部分,“DirectByteBuffer”调用“freeMemory”来释放该内存。 (3认同)