System.arraycopy不是零拷贝,因其需实际读取源数组并写入目标数组,发生真实内存拷贝;零拷贝指跳过CPU搬运、不产生副本,如transferTo或DirectByteBuffer共享。

System.arraycopy 不能实现零拷贝,它本质上是内存内数据的高效复制(浅层复制),需要实际读取源数组内容、写入目标数组,必然发生内存拷贝。所谓“零拷贝”(zero-copy)是指在数据传输过程中避免 CPU 参与数据搬运、不产生额外副本,常见于 I/O 层(如 FileChannel.transferTo、ByteBuffer 的堆外视图、Linux 的 sendfile 等),而非数组间操作。
为什么 arraycopy 不是零拷贝
System.arraycopy 是 JVM 提供的本地方法,底层通常由 JIT 编译为高度优化的内存块拷贝指令(如 rep movsb 或向量化 load/store)。但它仍需:
- 从源数组起始地址逐字节/字读取数据;
- 将数据写入目标数组对应偏移位置;
- 涉及真实内存读写和缓存行填充,存在带宽占用和延迟。
这与“零拷贝”的核心目标——跳过数据复制环节,直接让接收方访问原始数据内存区域——根本不同。
Java 中接近“零开销共享”的可行方式
若目标是避免重复分配和拷贝大数组,可考虑以下更贴近零拷贝思想的实践:
-
共享同一数组引用:不创建新数组,而是传递原数组及逻辑区间(offset + length),配合自定义容器或视图类封装,例如:
new ArrayView(originalArray, 100, 500); // 仅持引用+元数据 -
使用 ByteBuffer.allocateDirect + array()(谨慎):堆外缓冲区可跨 NIO 操作复用,但调用
array()会触发拷贝回堆内,真正零拷贝需全程使用ByteBuffer接口操作,不落地为普通数组。 - Unsafe.arrayBaseOffset + 偏移计算(不推荐):通过反射获取数组内存首地址并手动计算元素位置,绕过边界检查。但这破坏安全性、依赖 JVM 实现、无法跨版本保证,且仍不是零拷贝——只是跳过了 Java 层校验,数据访问本身仍发生。
什么场景下 arraycopy 是最优选择?
当确实需要一份独立、可控、类型安全的数组副本时,System.arraycopy 是 JVM 内最高效的拷贝手段:
- 比 for 循环快数倍(尤其大数据量,JIT 可自动向量化);
- 支持任意类型数组(对象/基本类型)、跨数组拷贝、重叠区域处理(类似 memmove);
- 无额外对象分配(目标数组需预先创建)。
例如:从 ArrayList 内部数组提取子序列、实现 ring buffer 的数据迁移、归并排序中的临时合并段。
真正的零拷贝在 Java 中如何体现?
典型路径是绕过 JVM 堆内存参与数据流转:
-
文件到网络:用
FileChannel.transferTo(ReadableByteChannel),由操作系统在内核空间完成 DMA 传输,不经过用户态内存; -
堆外内存共享:多个
ByteBuffer通过slice()共享同一块DirectByteBuffer底层内存,逻辑分片但物理不复制; - 序列化优化:Kryo/FST 等库结合 Unsafe 直接读写对象内存布局,避免中间 byte[] 创建,但仍非严格零拷贝(有解析开销)。










