system.arraycopy大规模迁移快而稳定的关键在于迁移前的数据组织与约束控制:目标数组须容量充足、源目标类型严格兼容、所有索引参数运行前校验;典型场景包括缓存池复用、arraylist扩容、ringbuffer前移;性能优化需基本类型优先、length用常量、避免多线程争抢同一dest。

System.arraycopy 实现大规模数据迁移,快是事实,但“快得稳定”靠的不是调用那行代码,而是迁移前的数据组织和运行时约束控制。它不分配内存、不创建对象、不校验元素内容,只做地址级搬运——所以性能天花板高,容错空间低。
迁移前必须做足三件事
缺一不可,否则运行时直接抛异常,没有商量余地:
- 目标数组必须已存在且容量充足:比如要搬 50 万条 long 数据,dest 数组长度至少为 destPos + 500000;少一个位置就触发
ArrayIndexOutOfBoundsException - 源与目标类型严格兼容:long[] 只能拷到 long[];String[] 可以拷到 Object[],但反过来会报
ArrayStoreException;int[] 和 Integer[] 完全不兼容,别试 - 所有索引参数必须运行前校验:建议封装工具方法,用
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 是“毫秒级魔法”:复制 1GB int 数组(约 2.5 亿元素)在主流服务器上通常需 20–80ms,受内存带宽限制,不可能稳进 1ms
- 在 JMH 测量时没预热、没隔离 JIT 干扰,或把 GC STW 时间算进复制耗时
- 对小数组(≤16 元素)滥用:JNI 调用开销可能超过简单 for 循环
- 想跨类型转换(如 int[] → String[])却硬套 arraycopy:它不做类型转换,只做内存块平移











