System.arraycopy 的高性能源于 JVM 底层绕过字节码,直接调用 native 内存指令(如 REP MOVSQ、MOVAPS)实现块拷贝,并依数组长度自动选用寄存器/SIMD/DMA策略,支持重叠拷贝与跨内存区域安全搬运。

System.arraycopy 的高性能本质,不在 Java 代码里,而在 JVM 底层对内存的直接调度——它跳过 Java 虚拟机的字节码执行路径,由 JVM 在运行时调用平台原生的 C/C++ 函数(如 memmove 或 memcpy),完成跨内存区域的块级拷贝。
底层不是“调用函数”,而是映射到硬件指令
Java 层写的 System.arraycopy(src, 0, dest, 0, len),在 JIT 编译后,往往不生成循环字节码,而是直接翻译成 CPU 级别的一条或多条高效指令:
- 在 x86 架构上,可能展开为
REP MOVSQ(一次复制 8 字节)或向量化指令(如 SSE2 的MOVAPS、AVX2 的VMOVDQA) - 现代 JVM(如 HotSpot)会根据数组长度自动选择策略:小数组用寄存器逐段搬;中等数组用 SIMD 批量加载/存储;超大数组甚至触发 DMA 类似语义(用户态零拷贝感知)
- 它支持重叠拷贝(
src和dest内存区域有交集),此时 JVM 自动选用memmove而非memcpy,避免数据覆盖错误
为什么能跨内存区域且安全
所谓“跨内存区域”,指源数组和目标数组在堆中位于不同地址段(比如老年代 → 新生代扩容、不同 ByteBuffer 的 backing array 之间搬运)。System.arraycopy 安全实现依赖三重保障:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- JVM 在调用前一次性完成全部边界检查:
srcPos、destPos、length是否越界,src和dest是否为 null,类型是否兼容——失败则抛异常,成功后才进入 native 搬运 - native 层不处理 Java 对象头或 GC 标记位,只按字节偏移做纯内存搬运;GC 线程通过写屏障(write barrier)确保搬运过程中对象引用不被误回收
- 对于对象数组,它只复制引用值(8 字节地址),不触发对象构造或 finalize;基本类型数组则按实际宽度(int=4B,long=8B)整块搬运,无装箱开销
对比手动循环,省掉的不只是“for”
手写 for 循环看似简单,但每轮迭代都隐含不可忽略的成本:
立即学习“Java免费学习笔记(深入)”;
- 每次访问
src[i]和dest[i]都需 JVM 做数组边界检查(即使 JIT 能优化部分,也无法 100% 消除) - 引用类型还要做 store-check(确保子类对象可存入父类数组),基本类型要处理符号扩展、大小端适配等
- 无法利用 CPU 预取(prefetch)、缓存行对齐(cache line alignment)、非临时存储(non-temporal store)等底层优化
- 而 System.arraycopy 把这些全部交给 native 层统一决策:JVM 知道 src 和 dest 的确切内存布局、对齐状态、是否在大页(HugePage)内,从而选最优 memcpy 变体
你可以这样验证它的“跨区域”能力
写个简单实验,观察不同内存分布下的行为一致性:
- 创建两个大数组(比如各 10MB),让它们大概率分配在堆中不相邻区域(可用
-XX:+UseG1GC -Xmx4g加大堆并多次分配触发碎片) - 用
System.arraycopy完成拷贝,再用 JOL(Java Object Layout)工具查看两数组arrayBaseOffset,确认地址差远大于数组长度 - 开启
-XX:+PrintAssembly(配合 hsdis),搜索汇编输出里的rep movsq或vpmovzxbd等指令,即证明已落地到硬件级搬运


















