jvm java.lang.System和Unsafe之间的性能差异

8tntrjer  于 2022-11-07  发布在  Java
关注(0)|答案(1)|浏览(202)

SystemUnsafe都提供了一些重叠的功能(例如,System.arraycopy_UNSAFE.copyMemory)。
在实现方面,看起来两者都依赖于jni,这是正确的说法吗?(我可以找到unsafe.cpp,但在JVM源代码中找不到相应的arraycopy实现)。
另外,如果两者都依赖于JNI,我能说两者的调用开销是相似的吗?
我知道Unsafe可以操纵offheap内存,但为了进行比较,我们在这里将上下文限制在onheap内存上。
谢谢你的回答。

vmdwslir

vmdwslir1#

System.arraycopyUnsafe.copyMemory都是HotSpot内部函数。这意味着,JVM在从JIT编译的方法调用这些方法时不使用JNI实现。相反,它会用特定于体系结构的优化汇编代码来替换调用。
您可以在stubGenerator_<arch>.cpp中找到源代码。
下面是一个简单的JMH基准测试:

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;

import java.util.concurrent.ThreadLocalRandom;

import static one.nio.util.JavaInternals.byteArrayOffset;
import static one.nio.util.JavaInternals.unsafe;

@State(Scope.Benchmark)
public class CopyMemory {

    @Param({"12", "123", "1234", "12345", "123456"})
    int size;

    byte[] src;
    byte[] dst;

    @Setup
    public void setup() {
        src = new byte[size];
        dst = new byte[size];
        ThreadLocalRandom.current().nextBytes(src);
    }

    @Benchmark
    public void systemArrayCopy() {
        System.arraycopy(src, 0, dst, 0, src.length);
    }

    @Benchmark
    public void unsafeCopyMemory() {
        unsafe.copyMemory(src, byteArrayOffset, dst, byteArrayOffset, src.length);
    }
}

这证实了两种方法的性能相似:

Benchmark                    (size)  Mode  Cnt     Score    Error  Units
CopyMemory.systemArrayCopy       12  avgt   16     5.294 ±  0.162  ns/op
CopyMemory.systemArrayCopy      123  avgt   16     7.057 ±  0.406  ns/op
CopyMemory.systemArrayCopy     1234  avgt   16    18.761 ±  0.492  ns/op
CopyMemory.systemArrayCopy    12345  avgt   16   353.386 ±  3.627  ns/op
CopyMemory.systemArrayCopy   123456  avgt   16  5234.125 ± 57.914  ns/op
CopyMemory.unsafeCopyMemory      12  avgt   16     5.028 ±  0.120  ns/op
CopyMemory.unsafeCopyMemory     123  avgt   16     8.055 ±  0.405  ns/op
CopyMemory.unsafeCopyMemory    1234  avgt   16    19.776 ±  0.523  ns/op
CopyMemory.unsafeCopyMemory   12345  avgt   16   353.549 ±  5.878  ns/op
CopyMemory.unsafeCopyMemory  123456  avgt   16  5246.298 ± 65.427  ns/op

如果您使用-prof perfasm分析器运行此JMH基准测试,您将看到这两种方法都归结为完全相同的汇编循环:


# systemArrayCopy

  0.64%   ↗   0x00007fa95d4336d0:   vmovdqu -0x38(%rdi,%rdx,8),%ymm0
  2.81%   │   0x00007fa95d4336d6:   vmovdqu %ymm0,-0x38(%rsi,%rdx,8)
  5.67%   │   0x00007fa95d4336dc:   vmovdqu -0x18(%rdi,%rdx,8),%ymm1
 69.64%   │   0x00007fa95d4336e2:   vmovdqu %ymm1,-0x18(%rsi,%rdx,8)
 15.28%   │   0x00007fa95d4336e8:   add    $0x8,%rdx
          ╰   0x00007fa95d4336ec:   jle    Stub::jbyte_disjoint_arraycopy+112 0x00007fa95d4336d0

# unsafeCopyMemory

  1.08%   ↗   0x00007f2d39833af0:   vmovdqu -0x38(%rdi,%rdx,8),%ymm0
  3.09%   │   0x00007f2d39833af6:   vmovdqu %ymm0,-0x38(%rcx,%rdx,8)
  5.78%   │   0x00007f2d39833afc:   vmovdqu -0x18(%rdi,%rdx,8),%ymm1
 66.44%   │   0x00007f2d39833b02:   vmovdqu %ymm1,-0x18(%rcx,%rdx,8)
 19.00%   │   0x00007f2d39833b08:   add    $0x8,%rdx
          ╰   0x00007f2d39833b0c:   jle    Stub::jlong_disjoint_arraycopy+48 0x00007f2d39833af0

在Java堆中处理正则数组时,完全不需要使用Unsafe API,标准的System.arraycopy已经做了很好的优化,JDK类库本身几乎到处都使用System.arraycopy,包括StringBuilderArrayListByteArrayOutputStream等。

相关问题