数组拷贝性能差异源于底层机制、数据规模和jvm环境,而非是否拷贝本身;实测百万级整型数组耗时排序为:system.arraycopy(0.1–0.3ms)< clone()(0.15–0.4ms)< arrays.copyof(0.2–0.5ms)< for循环(0.8–2.5ms)。

数组拷贝的执行时长差异,主要取决于底层机制、数据规模和 JVM 运行环境,而不是“要不要拷贝”这个动作本身。选错方式,小数组可能慢几倍,大数组可能卡顿上百毫秒甚至触发 GC 暂停。
核心方法耗时排序(实测 100 万元素级)
按典型 Java 场景(JDK 17+,预热后,无 GC 干扰)实测,耗时由低到高排列:
- System.arraycopy:最快,JVM 内置本地方法,绕过 Java 层边界检查与循环开销,耗时约 0.1–0.3 ms(整型数组);
- clone():本质调用 arraycopy,但多一层对象分发开销,慢约 20%–40%,耗时约 0.15–0.4 ms;
- Arrays.copyOf:内部封装了 arraycopy,但额外做长度校验和新数组分配,耗时略高,约 0.2–0.5 ms;
- for 循环赋值:纯 Java 层执行,受 JIT 编译影响大,小数组尚可,百万级明显拖慢,耗时约 0.8–2.5 ms;
- Buffer.BlockCopy(C#)或 Unsafe.copyMemory(Java):绕过安全检查,对齐内存下接近硬件带宽极限,但使用门槛高、易出错,一般场景不推荐。
对象数组拷贝:浅 vs 深,不是快慢问题,是需不需要的问题
拷贝 List
- 若只是临时传参、读取、缓存,用 new ArrayList(list) 或 list.toArray()——这是浅拷贝,只复制引用,毫秒内完成;
- 若必须隔离状态(如修改副本不影响原对象),才考虑深拷贝;此时耗时跃升:手写 get/set 约 10–15 ms,Cglib BeanCopier 约 25–40 ms,Jackson 序列化则达 1200+ ms;
- 多数业务逻辑其实不需要深拷贝,盲目套用 copyProperties 或 JSON 序列化,等于给每条请求加几十毫秒隐形延迟。
大数组迁移:arraycopy 不是银弹,关键在规避复制
1GB 数组用 arraycopy 复制,实测通常需 20–80 ms,无法做到“毫秒级”。真正提速要跳出“复制”思维:
- 能传引用就不拷贝——确认源数组后续不被修改;
- 数据来自文件?改用 MappedByteBuffer,实现零拷贝加载;
- 必须分块处理?单线程分块比多线程更稳,因 cache line 争用常导致并行反而更慢;
- 堆空间不足会触发 Young GC,建议 -Xms = -Xmx 预分配,并搭配 G1GC 限 pause 时间。
别被测试误导:注意测量前提
很多“for 比 clone 慢 3 倍”的结论,在真实服务中未必成立,因为:
- 未预热 JIT,首次运行 for 循环可能被解释执行,arraycopy 却始终走 native;
- 没关闭 GC 日志或忽略 STW,把一次 10ms 的 GC 暂停算进复制耗时;
- 测试数组太小(如 100 元素),各方法差异被纳秒级抖动掩盖,失去参考价值;
- 用 System.currentTimeMillis() 而非 System.nanoTime(),精度不够,尤其对 sub-ms 操作。











