system.arraycopy 比 for 循环快是因为它是 jvm 内置 native 方法,直接内存操作、跳过字节码开销,并可能用 simd 指令加速;只支持同类型数组间拷贝,不触发 gc,纯引用复制,参数易错需注意顺序和边界检查。

System.arraycopy 为什么比 for 循环快
因为它是 JVM 内置的本地方法,直接操作内存地址,跳过了 Java 字节码的逐元素访问开销。for 循环拷贝数组时,每次读写都要经过边界检查、类型校验、栈帧压入/弹出;System.arraycopy 把整块内存当“黑盒”搬走,JVM 还可能用 SIMD 指令(如 rep movsb)加速。
常见错误现象:ArrayStoreException 或 IndexOutOfBoundsException 不是抛在调用处,而是由 JVM 在 native 层检测后包装抛出——这说明它根本没走 Java 的数组访问逻辑。
- 只支持同类型数组间拷贝(源和目标必须兼容,比如
Object[]→String[]会失败) - 不触发 GC 分配新对象,也不调用任何构造器或 setter,纯内存复制
- 对基本类型数组(
int[]、byte[])优势最明显;对象数组只是复制引用,速度也快,但语义上不是深拷贝
System.arraycopy 参数顺序容易搞反
四个参数:源数组、源起始索引、目标数组、目标起始索引、长度。最容易错的是把「源起始」和「目标起始」位置记混,或者漏掉长度参数——编译器不会报错,但运行时直接越界或覆盖错位置。
典型错误场景:想从 src[2] 开始拷 5 个元素到 dst[0],却写成 System.arraycopy(src, 0, dst, 2, 5),结果 dst 前两个元素被意外覆盖。
立即学习“Java免费学习笔记(深入)”;
- 记住口诀:“源、源偏移、目标、目标偏移、数几个”
- 长度参数必须显式传,不能省略;它不是“拷到目标末尾”,而是“拷多少个”
- 所有索引和长度都要求 ≥ 0,且
源偏移 + 长度 ≤ 源数组长度,目标偏移 + 长度 ≤ 目标数组长度,否则抛IndexOutOfBoundsException
和 Arrays.copyOf 对比:什么时候该用哪个
Arrays.copyOf 底层其实就封装了一次 System.arraycopy,但它多做了一件事:分配一个新数组。所以如果你已经有目标数组,且大小合适,直接用 System.arraycopy 更轻量;如果需要扩容、缩容或生成副本,Arrays.copyOf 更安全简洁。
性能影响:每次 Arrays.copyOf 都触发一次堆分配,GC 压力略高;而 System.arraycopy 零分配,适合高频、小粒度拷贝(比如 ring buffer 移动、排序算法中的临时交换)。
- 要拷进已有数组 → 用
System.arraycopy - 要生成新数组且长度可能变 → 用
Arrays.copyOf(或Arrays.copyOfRange) -
Arrays.copyOf(x, x.length)等价于System.arraycopy(x, 0, 新数组, 0, x.length),但前者多一次 new 操作
注意 native 方法的隐含限制
System.arraycopy 是 JVM 实现相关的 native 方法,不保证跨平台行为完全一致——虽然主流 JDK(HotSpot、OpenJ9)都做了高度优化,但某些嵌入式或历史 JVM 可能降级为 Java 层模拟实现,性能骤降。
更关键的是:它不支持跨类加载器的数组类型兼容性判断。比如两个不同 ClassLoader 加载的 String 类,即使名字一样,System.arraycopy 也会拒绝拷贝并抛 ArrayStoreException。
- 不要在反射动态生成类、OSGi 或模块化环境里假设类型“看起来一样就能拷”
- 调试时如果发现拷贝突然变慢,先确认是否触发了 fallback 到 Java 层实现(可通过 JFR 或 -XX:+PrintCompilation 观察)
- 它不处理 null 元素的特殊逻辑,也不做任何序列化/反序列化,就是裸拷贝——这点和
clone()方法语义不同










