System
和Unsafe
都提供了一些重叠的功能(例如System.arraycopy
v.s _UNSAFE.copyMemory
)。
就实现而言,看起来两者都依赖于jni
,这是正确的语句吗?(我可以找到unsafe.cpp,但在JVM源代码中找不到相应的arraycopy
实现)。
另外,如果两者都依赖于JNI,我是否可以说两者的调用开销是相似的呢?
我知道Unsafe
可以操作offheap
内存,但为了比较起见,让我们在onheap
内存上限制上下文。
谢谢你的回答。
发布于 2022-08-26 17:14:00
System.arraycopy
和Unsafe.copyMemory
都是HotSpot的本质。这意味着,JVM在从JIT编译的方法调用这些方法时不使用JNI实现。相反,它将调用替换为特定于体系结构的优化程序集代码。
您可以在stubGenerator_.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堆中使用常规数组时,绝对不需要使用不安全的API。标准System.arraycopy
进行了很好的优化。JDK类库本身几乎处处使用System.arraycopy
,包括StringBuilder
、ArrayList
、ByteArrayOutputStream
等。
https://stackoverflow.com/questions/73476695
复制相似问题