system.arraycopy 是 native 方法,因需绕过 java 运行时检查、直接调用底层内存指令实现高效数组拷贝,其参数校验一次性完成,随后交由 jvm 本地代码执行无边界检查的纯内存搬运。

System.arraycopy 是 Java 中真正“贴近硬件”的数组操作入口,它的 native 语义不是指“写在 Java 里”,而是指整个执行过程跳出了 Java 字节码解释与常规运行时管控,由 JVM 直接委托给底层 C/C++ 实现,最终映射到操作系统级内存指令。
它为什么必须是 native
Java 层的数组访问(如 a[i])每次都要做三项检查:空指针校验、索引越界检查、类型兼容性验证。这些检查在循环中重复发生,无法消除。而 arraycopy 把全部合法性校验集中到调用入口一次性完成——源/目标非空、起始位置合法、长度非负、srcPos+length ≤ src.length、destPos+length ≤ dest.length、组件类型可赋值……校验通过后,JVM 就彻底“放手”,交由本地代码执行纯内存搬运。
这个交接点就是 native 的核心语义:脱离 Java 执行引擎,进入受信任的、无边界检查的、可直接调度 CPU 块复制指令(如 x86 的 rep movsb 或 ARM 的 ldp/stp 批量加载存储)的上下文。
native 背后的真实调用链
从你写下那一行 Java 代码开始,实际发生了:
宝塔面板11.3.0是一款针对Linux服务器设计的可视化管理工具,通过重构核心模块实现资源占用显著降低,尤其适合低配置服务器环境。它将复杂的命令行操作转化为直观的图形界面,帮助开发者快速完成网站部署、环境配置及日常运维工作,无需专业技术背景即可高效管理服务器。
- JVM 查找注册的 JNI 函数入口
JVM_ArrayCopy(位于hotspot/src/share/vm/prims/jvm.cpp) - 根据数组类型(
int[]/Object[])、是否同类型、是否重叠,分发到具体实现:typeArrayOopDesc::copy_array或objArrayOopDesc::copy_array - 最终跳转至平台专用的汇编 stub(如
copy_memory),调用memcpy(无重叠)或memmove(支持重叠,自动选前后向拷贝)
整个过程不经过解释器、不触发 JIT 编译决策、不插入 GC 写屏障(尤其在老年代间拷贝时显著减少卡表更新),也没有对象包装开销。
native 带来的关键行为特征
正因为是 native,它表现出几个不可绕过的行为事实:
-
零容忍参数错误:任一参数违法(如负 length、越界索引),立即抛出
ArrayIndexOutOfBoundsException或NullPointerException,不会尝试“尽力而为” -
类型检查发生在运行时,且只做一次:编译期不检查,但 native 层会严格比对源/目标数组的 component type,不兼容就抛
ArrayStoreException(例如Object[] → String[]失败,String[] → Object[]成功) - 浅拷贝是硬性语义:对引用类型数组,复制的是地址值本身,不是对象实例;native 层不做深克隆,也不递归追踪引用链
- 不保证内存可见性顺序:它不插入 volatile 语义或内存屏障,多线程下需配合同步机制使用
和“看起来像 native”的方法有本质区别
像 Arrays.copyOf 或 clone(),虽然内部也调用 arraycopy,但它们自身是纯 Java 方法:要创建新数组、做参数预处理、处理扩容逻辑、可能触发额外 GC。这些封装层增加了间接成本。而直接调用 arraycopy,就是把控制权交给 JVM 底层——你提供地址、偏移、长度,它就搬运,不多问,不加戏。
这就是 native 的本意:不是“用了 C 写”,而是“让 Java 暂时退场”。










