system.arraycopy是jvm层面的内存块搬运指令,非普通复制函数;它高效因跳过字节码循环与逐元素校验,严格因参数错误即抛异常;迁移前必须确保目标数组存在且容量充足、源目标类型严格兼容、所有索引参数运行前校验。

System.arraycopy 不是“复制函数”,而是 JVM 层面的内存块搬运指令——它快,是因为跳过 Java 字节码循环和逐元素校验;它严,是因为参数错一个就抛异常,不警告、不静默修复。真正决定业务逻辑迁移成败的,不是会不会写那行代码,而是迁移前的数据组织是否到位。
迁移前必须做足三件事
缺一不可,否则运行时直接崩溃:
-
目标数组必须已存在且容量充足:例如要迁移 80 万条 int 数据,dest 数组长度至少为
destPos + 800000;少一个位置就触发ArrayIndexOutOfBoundsException -
源与目标类型严格兼容:int[] 只能拷到 int[] 或 Integer[](会报
ArrayStoreException);String[] 可以拷到 Object[],但反过来绝对不行;int[] 和 long[] 完全不兼容,别尝试 -
所有索引参数必须运行前校验:建议封装工具方法,用
Objects.requireNonNull()检查 null,用Math.min()控制 length 上限;例如:if (srcPos src.length || destPos dest.length) throw new IllegalArgumentException();
典型业务场景中的高效写法
不是所有“大数组”都适合直接 arraycopy,关键看数据流动是否可预判、是否复用已有容器:
-
缓存池复用迁移:网络收包后,只把有效 payload(如从
byte[] packet索引 16 开始的 2048 字节)搬入固定大小的byte[8192]缓冲区:System.arraycopy(packet, 16, buffer, 0, 2048); -
ArrayList 底层扩容:旧数组满时,new 一个更大数组,再整块迁移:
int[] newData = new int[old.length * 2];<br>System.arraycopy(old, 0, newData, 0, old.length);
-
RingBuffer 前移腾空间:读指针后还有未处理数据,整体前移到缓冲区开头释放头部空间:
System.arraycopy(buffer, readPos, buffer, 0, unreadCount);
JVM 自动按安全顺序搬运,不怕重叠
性能优化的关键细节
真正拉开速度差距的,往往在参数和环境选择上:
- 基本类型优先:byte[]、int[]、long[] 迁移最快,无 GC write barrier,JVM 可直发 SIMD 指令;对象数组只拷引用,也快,但后续修改会影响源
-
length 尽量用常量:写
1024比写变量n更易被 JIT 内联优化;小批量(≤64 元素)优势不明显,建议阈值设为 256 起启用 - 避免多线程争抢同一 dest:arraycopy 单次调用原子,但不线程安全;若多个线程同时往同一数组不同区域写,仍可能撕裂;应加锁,或改用“新数组 + 原子引用替换”模式
常见踩坑与规避方式
这些错误高频出现,且一旦发生难以定位:
-
误把 arraycopy 当 Arrays.copyOf 用:它不分配内存、不创建对象;想完整复制成新数组,请用
Arrays.copyOf(src, src.length),否则多一次无效分配 -
跨类型混用:int[] 往 Integer[] 拷、String[] 往 byte[] 拷,都会在运行时报
ArrayStoreException;注意泛型擦除不影响判断,List<string>[]</string>可拷到Object[] -
忽略 length=0 的合法用途:传入
length=0是合法空操作,可用于快速校验边界是否安全,无需额外 if 判断
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











