system.arraycopy更快,因其绕过java运行时冗余检查:一次性参数校验后直接内存块搬运,获jit内联与simd优化;而for循环需重复边界检查、类型校验及字节码开销。

System.arraycopy 通常比手动 for 循环快得多,但这个“快”不是玄学,而是 JVM 在底层做了大量针对性优化的结果。它不是靠“写得更底层”,而是靠“绕开 Java 运行时的冗余检查”。关键在于:什么时候快、快多少、为什么快、以及怎么用才不白费这波优化。
为什么 System.arraycopy 更快?核心在三处剥离
手动 for 循环拷贝每一步都要走完整 Java 层语义:
- 每次
a[i]都隐式触发一次数组边界检查(源数组长度校验 + 索引比对) - 每次
b[i] = x又要再做一次目标数组边界检查 + 类型兼容性校验(比如往 String[] 里塞 Integer 就会失败) - 循环本身带来跳转、计数、栈帧维护等字节码开销,JIT 难以完全向量化
而 System.arraycopy 的路径完全不同:
- 参数一次性校验:源/目标非空、起始索引合法、长度非负、类型兼容——全在进入 native 前完成
- 之后直接调用类似
memmove的内存块搬运函数,不经过任何 Java 字节码 - JIT 编译器把它当“白名单原子操作”,可内联、可启用 SIMD 指令(如一次搬 16 字节)、能配合 GC 卡表做写屏障优化
性能差距到底有多大?看数组长度说话
差距不是固定倍数,而是随规模非线性扩大:
- 数组长度 ≤ 4:for 循环可能更快——JVM 启动 arraycopy 的 JNI 开销 > 拷贝收益;小数组还容易被 JIT 栈上分配优化
- 长度 20–1000:arraycopy 开始稳定胜出,优势随长度增长加速明显
- 长度 ≥ 10⁵(十万级):实测差距常达 3–5 倍;对 byte[] 或 int[],甚至逼近硬件 memcpy 极限带宽
例如 JDK 17 下拷贝 100 万个 int 元素:
-
System.arraycopy:约 0.03–0.05 ms - 手动 for 循环(已缓存数组引用):约 0.12–0.18 ms
- 增强 for 循环(for-each):约 0.25–0.40 ms(额外迭代器开销)
怎么用才真正发挥 arraycopy 的优势?
写错参数或误用场景,反而会让性能反降:
- 目标数组必须预先创建且长度足够,否则抛
IndexOutOfBoundsException或ArrayStoreException - 源与目标类型必须兼容:不能把
String[]拷到Integer[],但可以拷到Object[] - 避免“为省事”在循环里反复调用 arraycopy 拷 1 个元素——JNI 调用开销会被放大
- 若 src == dest(自拷贝),arraycopy 自动处理重叠逻辑(类似 memmove),无需手动判断方向
- 不要包装成泛型方法再调用——泛型擦除可能引入桥接方法,破坏 JIT 内联机会
替代方案怎么选?Arrays.copyOf 和 clone()
日常开发中,多数时候不该裸调 System.arraycopy:
-
Arrays.copyOf(src, newLength):底层就是封装了 arraycopy,自动分配新数组,适合扩容+拷贝场景 -
arr.clone():对一维数组也调用 arraycopy,语法最简洁,但仅支持一维,且返回类型是Object,需强制转型 - 别和
BeanUtils.copyProperties比——那是对象属性拷贝,和数组内存拷贝不在一个维度











