java System.arraycopy() 对应Hotspot里面的源码
public class Copy {
public static void main(String[] args) throws InterruptedException {
String x = "123456789";
System.out.println("start");
Thread.sleep(3000);
byte[] bytes1 = x.getBytes();
byte[] bytes = new byte[32];
System.arraycopy(bytes1, 0, bytes, 0, 8);//本质是将一个内存地址的值复制到另一个内存地址
}
}
// System.arraycopy 本地方法
@HotSpotIntrinsicCandidate
public static native void arraycopy(Object var0, int var1, Object var2, int var3, int var4);
jvm.cpp 307行为本地方法入口
JVM_ENTRY(void, JVM_ArrayCopy(JNIEnv *env, jclass ignored, jobject src, jint src_pos,
jobject dst, jint dst_pos, jint length))
JVMWrapper("JVM_ArrayCopy");
// Check if we have null pointers
if (src == NULL || dst == NULL) {
THROW(vmSymbols::java_lang_NullPointerException());
}
arrayOop s = arrayOop(JNIHandles::resolve_non_null(src));
arrayOop d = arrayOop(JNIHandles::resolve_non_null(dst));
assert(s->is_oop(), "JVM_ArrayCopy: src not an oop");
assert(d->is_oop(), "JVM_ArrayCopy: dst not an oop");
// Do copy
s->klass()->copy_array(s, src_pos, d, dst_pos, length, thread);
JVM_END
//进入klass()->copy_array(s, src_pos, d, dst_pos, length, thread);
void TypeArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, TRAPS) {
//省略了其他检查逻辑
int l2es = log2_element_size();
int ihs = array_header_in_bytes() / wordSize;
char* src = (char*) ((oop*)s + ihs) + ((size_t)src_pos << l2es);
char* dst = (char*) ((oop*)d + ihs) + ((size_t)dst_pos << l2es);
Copy::conjoint_memory_atomic(src, dst, (size_t)length << l2es);
}
下面断点截图同src 地址为 0xff0bd920 值为123456789 确实和java代码里的值一样
dst代表目标地址 地址为 0xff0bd940
继续进入函数
// Copy bytes; larger units are filled atomically if everything is aligned.
void Copy::conjoint_memory_atomic(void* from, void* to, size_t size) {
address src = (address) from;
address dst = (address) to;
uintptr_t bits = (uintptr_t) src | (uintptr_t) dst | (uintptr_t) size;
//将 要复制数组的内存地址 和目的地址已经长度 进行或运算 在下面根据结果判断 是否是对齐的 8字节对齐 4字节对齐和2直接对齐 这里我刻意拷贝8个字节代码进入第一个分支
if (bits % sizeof(jlong) == 0) {
Copy::conjoint_jlongs_atomic((jlong*) src, (jlong*) dst, size / sizeof(jlong));
} else if (bits % sizeof(jint) == 0) {
Copy::conjoint_jints_atomic((jint*) src, (jint*) dst, size / sizeof(jint));
} else if (bits % sizeof(jshort) == 0) {
Copy::conjoint_jshorts_atomic((jshort*) src, (jshort*) dst, size / sizeof(jshort));
} else {
// Not aligned, so no need to be atomic.
Copy::conjoint_jbytes((void*) src, (void*) dst, size);
}
}
// bytes, jshorts, jints, jlongs, oops
// bytes, conjoint, not atomic on each byte (not that it matters)
static void conjoint_jbytes(void* from, void* to, size_t count) {
pd_conjoint_bytes(from, to, count);
}
static void pd_conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) {
#ifdef AMD64
_Copy_conjoint_jlongs_atomic(from, to, count);
接着进入
执行到jbe 0x9e6fa6会进入真正的拷贝逻辑 (注意此时 rax寄存器和rdi 寄存器里面保存的 要复制的内存地址的值)
下面是真正的复制逻辑 此时rsi寄存器的值还为改变 当执行完 movq 0x8(%rax,%rdx,8), %rsi
高亮的代码后rsi里面回存放要复制的值(12345678)刚好为8个字节
执行结果 rsi 已经为要复制的值 0x3837363534333231 这里为什么不是12345678呢 因为 12345678的asc编码就是3132333435363738
movq %rsi,0x8(%rcx,%rdx,0) 的意思是将rsi寄存器里面的值给到 目标的内存地址 也就是 0xff0bd940 (从下方的调试窗口给出的寄存器值手动计算也是这个值)
这里我们输出一下 0xff0bd940 的值