精华内容
下载资源
问答
  • arraycopy

    2021-03-20 19:45:17
    下面是 System.arrayCopy的源代码声明 : public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 代码解释:  Object src : 原数组 int srcPos : 从元数据的起始位置开始 ...

    下面是 System.arrayCopy的源代码声明 : 

    public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
    代码解释:
      Object src : 原数组
       int srcPos : 从元数据的起始位置开始
      Object dest : 目标数组
      int destPos : 目标数组的开始起始位置
      int length  : 要copy的数组的长度
    

    比如 :我们有一个数组数据 byte[]  srcBytes = new byte[]{2,4,0,0,0,0,0,10,15,50};  // 源数组

                                        byte[] destBytes = new byte[5]; // 目标数组

    我们使用System.arraycopy进行转换(copy)
    
    System.arrayCopy(srcBytes,0,destBytes ,0,5)
    上面这段代码就是 : 创建一个一维空数组,数组的总长度为 12位,然后将srcBytes源数组中 从0位 到 第5位之间的数值 copy 到 destBytes目标数组中,在目标数组的第0位开始放置.
    那么这行代码的运行效果应该是 2,4,0,0,0,
    展开全文
  • System.arraycopy详解

    2021-04-12 15:02:45
    System.arraycopy arrays.copyof 下面分析一下最常用的System.arraycopy() 二、源码拜读 1、源码 2、参数分析 Object src : 原数组 int srcPos : 从元数据的起始位置开始 Object dest : 目标数组 int destPos : 目标...

    一、前言
    对数组的复制,有四种方法:

    for
    clone
    System.arraycopy
    arrays.copyof
    下面分析一下最常用的System.arraycopy()

    二、源码拜读
    1、源码

    2、参数分析
    Object src : 原数组
    int srcPos : 从元数据的起始位置开始
    Object dest : 目标数组
    int destPos : 目标数组的开始起始位置
    int length : 要copy的数组的长度
    三、深拷贝与浅拷贝
    如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力。

    1、简单类型(深拷贝)
    package com.guor.test.javaSE.collection;

    import java.util.Arrays;

    public class ArrayTest {

    public static void main(String[] args) {
    	getStringArrayFromObjectArray2();
    }
    
    private static void copySelf() {
    	int[] ids = { 1, 2, 3, 4, 5 };  
    	System.out.println(Arrays.toString(ids));
    	//System.arraycopy(src, srcPos, dest, destPos, length);
    	// 把从索引0开始的2个数字复制到索引为3的位置上  
    	System.arraycopy(ids, 0, ids, 3, 2);
    	System.out.println(Arrays.toString(ids));//[1, 2, 3, 1, 2]
    }
    
    private static void copyToOther() {
    	int[] ids = { 1, 2, 3, 4, 5 };  
    	//将数据的索引1开始的3个数据复制到目标的索引为0的位置上  
    	int[] other = new int[5];
    	System.arraycopy(ids, 1, other, 0, 3);
    	System.out.println(Arrays.toString(ids));//[1, 2, 3, 4, 5]深复制
    	System.out.println(Arrays.toString(other));//[2, 3, 4, 0, 0]
    }
    
    //如果是类型转换问题,获取整形
    private static void getIntegerArrayFromObjectArray() {
    	Object[] obj1 = { 1, 2, 3, "4", "5" }; 
    	Integer[] obj2 = new Integer[5];
    	
    	try {
    		System.arraycopy(obj1, 0, obj2, 0, obj1.length);
    	} catch (Exception e) {
    		System.out.println("transfer exception:"+e);
    	}
    	System.out.println(Arrays.toString(obj1));
    	System.out.println(Arrays.toString(obj2));
    }
    
    //获取Object数组中的字符串类型数据
    private static void getStringArrayFromObjectArray1() {
    	Object[] obj3 = { 1, 2, 3, "4", "5" }; 
    	String[] obj4 = new String[5];
    	try {
    		System.arraycopy(obj3, 2, obj4, 2, 3);
    	} catch (Exception e) {
    		//transfer exception:java.lang.ArrayStoreException
    		System.out.println("transfer exception:"+e);
    	}
    	System.out.println(Arrays.toString(obj3));
    	//[null, null, null, null, null]
    	System.out.println(Arrays.toString(obj4));
    }
    
    //获取Object数组中的字符串类型数据
    private static void getStringArrayFromObjectArray2() {
    	Object[] obj3 = { 1, 2, 3, "4", "5" }; 
    	String[] obj4 = new String[5];
    	try {
    		System.arraycopy(obj3, 3, obj4, 3, 2);
    	} catch (Exception e) {
    		System.out.println("transfer exception:"+e);
    	}
    	System.out.println(Arrays.toString(obj3));
    	//[null, null, null, 4, 5]
    	System.out.println(Arrays.toString(obj4));
    	obj3[3] = "zhangssan";
    	System.out.println("查看是浅复制还是深复制~~~~~");
    	System.out.println(Arrays.toString(obj3));
    	System.out.println(Arrays.toString(obj4));
    }
    

    }
    只有普通数据类型和String类型是深拷贝!

    2、二维数组(浅拷贝)
    //二维数组
    public static void twoArray() {
    int[] arr1 = {1, 2};
    int[] arr2 = {3, 4};
    int[] arr3 = {5, 6};

    int[][] src = new int[][]{arr1, arr2, arr3};
    
    print("原始模样:", src);
    int[][] dest = new int[3][];
    System.arraycopy(src, 0, dest, 0, 3);
    
    System.out.println("改变前");
    print("src = ", src);
    print("dest = ", dest);
    
    //原数组改变后观察新数组是否改变,改变->浅复制,不改变->深复制
    src[0][0] = -1;
    
    System.out.println("改变后");
    print("src = ", src);
    print("dest = ", dest);
    

    }

    //二维数组toString()
    private static void print(String string, int[][] arr) {
    System.out.print(string);
    for (int[] a : arr) {
    for (int i : a) {
    System.out.print(i + " “);
    }
    System.out.print(”,");
    }
    System.out.println();
    }
    二维数组属于浅拷贝,原始数组改变后,复制的数据也发生了改变!

    3、对象复制(深拷贝?)

    四、System.arraycopy是不安全的
    1、代码实例
    多线程对数组进行复制,看System.arraycopy线程是否安全?

    package com.guor.test.javaSE.collection;

    import java.util.Arrays;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;

    public class ArrayTest2 {
    private static int[] arrayOriginal = new int[1024 * 1024 * 10];
    private static int[] arraySrc = new int[1024 * 1024 * 10];
    private static int[] arrayDest = new int[1024 * 1024 * 10];
    private static ReentrantLock lock = new ReentrantLock();

    private static void modify() {
        for (int i = 0; i < arraySrc.length; i++) {
            arraySrc[i] = i + 1;
        }
    }
    
    private static void copy() {
        System.arraycopy(arraySrc, 0, arrayDest, 0, arraySrc.length);
    }
    
    private static void init() {
        for (int i = 0; i < arraySrc.length; i++) {
            arrayOriginal[i] = i;
            arraySrc[i] = i;
            arrayDest[i] = 0;
        }
    }
    
    private static void doThreadSafeCheck() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println("run count: " + (i + 1));
            init();
            Condition condition = lock.newCondition();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    condition.signalAll();
                    lock.unlock();
                    copy();
                }
            }).start();
    
    
            lock.lock();
            // 这里使用 Condition 来保证拷贝线程先已经运行了.
            condition.await();
            lock.unlock();
    
            Thread.sleep(2); // 休眠2毫秒, 确保拷贝操作已经执行了, 才执行修改操作.
            modify();
    
            // 如果 System.arraycopy 是线程安全的, 那么先执行拷贝操作, 再执行修改操作时, 不会影响复制结果, 因此 arrayOriginal 必然等于 arrayDist; 
            if (!Arrays.equals(arrayOriginal, arrayDest)) {
                throw new RuntimeException("System.arraycopy is not thread safe");
            }
        }
    }
    
    public static void main(String[] args) throws Exception {
        //doThreadSafeCheck();
        executeTime();
    }
    
    private static void executeTime() {
    	String[] srcArray = new String[1000000];
        String[] forArray = new String[srcArray.length];
        String[] arrayCopyArray  = new String[srcArray.length];
        
        //初始化数组
        for(int i  = 0 ; i  < srcArray.length ; i ++){
            srcArray[i] = String.valueOf(i);
        }
        
        long forStartTime = System.currentTimeMillis();
        for(int index  = 0 ; index  < srcArray.length ; index ++){
            forArray[index] = srcArray[index];
        }
        long forEndTime = System.currentTimeMillis();
        System.out.println("for方式复制数组:"  + (forEndTime - forStartTime));
        
        long arrayCopyStartTime = System.currentTimeMillis();
        System.arraycopy(srcArray,0,arrayCopyArray,0,srcArray.length);
        long arrayCopyEndTime = System.currentTimeMillis();
        System.out.println("System.arraycopy复制数组:"  + (arrayCopyEndTime - arrayCopyStartTime));
    }
    

    }
    2、代码思路分析
    arrayOriginal 和 arraySrc 初始化时是相同的, 而 arrayDist 是全为零的.

    启动一个线程运行 copy() 方法来拷贝 arraySrc 到 arrayDist 中.

    在主线程执行 modify() 操作, 修改 arraySrc 的内容. 为了确保 copy() 操作先于 modify() 操作, 我使用 Condition, 并且延时了两毫秒, 以此来保证执行拷贝操作(即System.arraycopy) 先于修改操作.

    根据第三点, 如果 System.arraycopy 是线程安全的, 那么先执行拷贝操作, 再执行修改操作时, 不会影响复制结果, 因此 arrayOriginal 必然等于 arrayDist; 而如果 System.arraycopy 是线程不安全的, 那么 arrayOriginal 不等于 arrayDist.

    3、控制台输出

    ————————————————
    版权声明:本文为CSDN博主「GooReey」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/guorui_java/article/details/113187970

    展开全文
  • } 2 数组拷贝System.arraycopy 调试代码V1 底层函数被频繁调用,无法分辨是不是这套代码的堆栈。 import java.util.*; public class ListTest1 { public static void main(String[] args) { List<String> ...

    OPENJDK源码:wget https://download.java.net/openjdk/jdk8/promoted/b132/openjdk-8-src-b132-03_mar_2014.zip

    1 ArrayList增长策略

    • 最小增长区间:10
    • 增长算法:new = old + old / 2
    • 实际增长点:10、15、22、33、49、73、109、163、244、366、548、823、1234

    也就是说增长到1000的数组如果没有事先指定大小,会发生13次Arrays.copyOf动作,拷贝代价多大?继续分析

        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    

    2 数组拷贝System.arraycopy

    调试代码V1

    底层函数被频繁调用,无法分辨是不是这套代码的堆栈。

    import java.util.*;
    
    public class ListTest1 {
        public static void main(String[] args) {
            List<String> stringArrayList = new ArrayList<>();
            for (int i = 0; i<100000; i++) {
                stringArrayList.add("hello");
            }
            System.out.println(stringArrayList.get(0));
        }
    }
    

    调试代码V2

    GDB看不到JAVA堆栈,用变量值定位所需堆栈

    public class SystemCopyTest {
        public static void main(String[] args) {
            char[] s = new char[6000];
            char[] d = new char[9000];
            for (int i = 79; i < 137; i++) {
                s[i] = (char)(i-14);
            }
            System.arraycopy(s, 79, d, 0, 58);
            System.out.println(d);
        }
    }
    

    输出

    [root@27cb1371dc0d ~]# /root/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/bin/javac SystemCopyTest.java
    [root@27cb1371dc0d ~]# /root/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/bin/java SystemCopyTest
    ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz
    

    堆栈分析

    JAVA Frames-0

    0层栈帧在JAVA中,进入JAVA堆栈

    源码

        public static native void arraycopy(Object src,  int  srcPos,
                                            Object dest, int destPos,
                                            int length);
    

    JVM Frames-1

    /root/openjdk/hotspot/src/share/vm/prims/jvm.cpp:310

    s->klass()->copy_array(s, src_pos, d, dst_pos, length, thread);

    JVM Frames-2

    /root/openjdk/hotspot/src/share/vm/oops/typeArrayKlass.cpp:155

    void TypeArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, TRAPS)

    注意打断点的技巧,底层函数会被很多地方调用,要找到关注的堆栈可以用值匹配

    b TypeArrayKlass::copy_array if (src_pos79&&length58)

    gdb

    Breakpoint 1, TypeArrayKlass::copy_array (this=0x100000210, s=0xf5a00000, src_pos=79, d=0xf5a02ef0, dst_pos=0, length=58, __the_thread__=0x7f905400b000)
        at /root/openjdk/hotspot/src/share/vm/oops/typeArrayKlass.cpp:130
    130   assert(s->is_typeArray(), "must be type array");
    (gdb) bt
    #0  TypeArrayKlass::copy_array (this=0x100000210, s=0xf5a00000, src_pos=79, d=0xf5a02ef0, dst_pos=0, length=58, __the_thread__=0x7f905400b000)
        at /root/openjdk/hotspot/src/share/vm/oops/typeArrayKlass.cpp:155
    #1  0x00007f905b070a47 in JVM_ArrayCopy (env=0x7f905400b210, ignored=0x7f905c8f86d8, src=0x7f905c8f86a8, src_pos=79, dst=0x7f905c8f86b8, dst_pos=0, length=58)
        at /root/openjdk/hotspot/src/share/vm/prims/jvm.cpp:310
    #2  0x00007f90451cdb51 in ?? ()
    #3  0x000000000000003a in ?? ()
    #4  0x00000000f5a00000 in ?? ()
    #5  0x00007f905c8f6aa8 in ?? ()
    #6  0x00000000f5a02ef0 in ?? ()
    #7  0x0000000000000004 in ?? ()
    #8  0x0000000000000000 in ?? ()
    

    源码

    void TypeArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, TRAPS) {
      assert(s->is_typeArray(), "must be type array");
    
      // Check destination
      if (!d->is_typeArray() || element_type() != TypeArrayKlass::cast(d->klass())->element_type()) {
        THROW(vmSymbols::java_lang_ArrayStoreException());
      }
    
      // Check is all offsets and lengths are non negative
      if (src_pos < 0 || dst_pos < 0 || length < 0) {
        THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
      }
      // Check if the ranges are valid
      if  ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length())
         || (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) {
        THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
      }
      // Check zero copy
      if (length == 0)
        return;
    
      // This is an attempt to make the copy_array fast.
      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);
    }
    

    JVM Frames-3(对齐)

    /root/openjdk/hotspot/src/share/vm/utilities/copy.cpp:45

    void Copy::conjoint_memory_atomic

    gdb

    (gdb) bt
    #0  Copy::conjoint_memory_atomic (from=0xf5a000ae, to=0xf5a02f00, size=116) at /root/openjdk/hotspot/src/share/vm/utilities/copy.cpp:46
    #1  0x00007f905b3f84df in TypeArrayKlass::copy_array (this=0x100000210, s=0xf5a00000, src_pos=79, d=0xf5a02ef0, dst_pos=0, length=58, __the_thread__=0x7f905400b000)
        at /root/openjdk/hotspot/src/share/vm/oops/typeArrayKlass.cpp:155
    #2  0x00007f905b070a47 in JVM_ArrayCopy (env=0x7f905400b210, ignored=0x7f905c8f86d8, src=0x7f905c8f86a8, src_pos=79, dst=0x7f905c8f86b8, dst_pos=0, length=58)
        at /root/openjdk/hotspot/src/share/vm/prims/jvm.cpp:310
    #3  0x00007f90451cdb51 in ?? ()
    #4  0x000000000000003a in ?? ()
    #5  0x00000000f5a00000 in ?? ()
    #6  0x00007f905c8f6aa8 in ?? ()
    #7  0x00000000f5a02ef0 in ?? ()
    #8  0x0000000000000004 in ?? ()
    #9  0x0000000000000000 in ?? ()
    

    对齐到8、4、2上,走不同的处理分支

    void Copy::conjoint_memory_atomic(void* from, void* to, size_t size = 116) {
      address src = (address) from = 4120903854
      address dst = (address) to = 4120915712
      uintptr_t bits = (uintptr_t) src | (uintptr_t) dst | (uintptr_t) size = 4120915966
    
    bits % sizeof(jlong) = 2
          
      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);
      }
    }
    

    JVM Frames-4

    /root/openjdk/hotspot/src/share/vm/utilities/copy.hpp

    static void conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count)

    gdb

    #0  Copy::conjoint_jshorts_atomic (from=0xf5a000ae, to=0xf5a02f00, count=58) at /root/openjdk/hotspot/src/share/vm/utilities/copy.hpp:134
    #1  0x00007f905ae18b36 in Copy::conjoint_memory_atomic (from=0xf5a000ae, to=0xf5a02f00, size=116) at /root/openjdk/hotspot/src/share/vm/utilities/copy.cpp:49
    #2  0x00007f905b3f84df in TypeArrayKlass::copy_array (this=0x100000210, s=0xf5a00000, src_pos=79, d=0xf5a02ef0, dst_pos=0, length=58, __the_thread__=0x7f905400b000)
        at /root/openjdk/hotspot/src/share/vm/oops/typeArrayKlass.cpp:155
    #3  0x00007f905b070a47 in JVM_ArrayCopy (env=0x7f905400b210, ignored=0x7f905c8f86d8, src=0x7f905c8f86a8, src_pos=79, dst=0x7f905c8f86b8, dst_pos=0, length=58)
        at /root/openjdk/hotspot/src/share/vm/prims/jvm.cpp:310
    #4  0x00007f90451cdb51 in ?? ()
    #5  0x000000000000003a in ?? ()
    #6  0x00000000f5a00000 in ?? ()
    #7  0x00007f905c8f6aa8 in ?? ()
    #8  0x00000000f5a02ef0 in ?? ()
    #9  0x0000000000000004 in ?? ()
    #10 0x0000000000000000 in ?? ()
    

    源码

      // jshorts,               conjoint, atomic on each jshort
      static void conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) {
        assert_params_ok(from, to, LogBytesPerShort);
        pd_conjoint_jshorts_atomic(from, to, count);
      }
    

    JVM Frames-5

    /root/openjdk/hotspot/src/os_cpu/linux_x86/vm/copy_linux_x86.inline.hpp:227

    static void pd_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count)

    gdb

    (gdb) bt
    #0  Copy::pd_conjoint_jshorts_atomic (from=0xf5a000ae, to=0xf5a02f00, count=58) at /root/openjdk/hotspot/src/os_cpu/linux_x86/vm/copy_linux_x86.inline.hpp:227
    #1  0x00007f905ae18cf0 in Copy::conjoint_jshorts_atomic (from=0xf5a000ae, to=0xf5a02f00, count=58) at /root/openjdk/hotspot/src/share/vm/utilities/copy.hpp:135
    #2  0x00007f905ae18b36 in Copy::conjoint_memory_atomic (from=0xf5a000ae, to=0xf5a02f00, size=116) at /root/openjdk/hotspot/src/share/vm/utilities/copy.cpp:49
    #3  0x00007f905b3f84df in TypeArrayKlass::copy_array (this=0x100000210, s=0xf5a00000, src_pos=79, d=0xf5a02ef0, dst_pos=0, length=58, __the_thread__=0x7f905400b000)
        at /root/openjdk/hotspot/src/share/vm/oops/typeArrayKlass.cpp:155
    #4  0x00007f905b070a47 in JVM_ArrayCopy (env=0x7f905400b210, ignored=0x7f905c8f86d8, src=0x7f905c8f86a8, src_pos=79, dst=0x7f905c8f86b8, dst_pos=0, length=58)
        at /root/openjdk/hotspot/src/share/vm/prims/jvm.cpp:310
    #5  0x00007f90451cdb51 in ?? ()
    #6  0x000000000000003a in ?? ()
    #7  0x00000000f5a00000 in ?? ()
    #8  0x00007f905c8f6aa8 in ?? ()
    #9  0x00000000f5a02ef0 in ?? ()
    #10 0x0000000000000004 in ?? ()
    #11 0x0000000000000000 in ?? ()
    

    源码

    static void pd_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) {
      _Copy_conjoint_jshorts_atomic(from, to, count);
    }
    

    JVM Frames-6(开始拷贝)

    hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.s

    _Copy_conjoint_jshorts_atomic(from, to, count)

    源码

            .p2align 4,,15
        .type    _Copy_arrayof_conjoint_jshorts,@function
        .type    _Copy_conjoint_jshorts_atomic,@function
    _Copy_arrayof_conjoint_jshorts:
    _Copy_conjoint_jshorts_atomic:
            movq     %rdx,%r8             # word count
            shrq     $2,%rdx              # qword count
            cmpq     %rdi,%rsi
            leaq     -2(%rdi,%r8,2),%rax  # from + wcount*2 - 2
            jbe      acs_CopyRight
            cmpq     %rax,%rsi
            jbe      acs_CopyLeft 
    

    gdb
    b _Copy_conjoint_jshorts_atomic 进入/root/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/lib/amd64/server/libjvm.so

    layout regs:打开寄存器监控
    disassemble:查看汇编代码si:单步i all-registers:打印所有寄存器i registers:打印寄存器

    关键寄存器
    
     rsi            0xf5a02f00   4120915712           ------> to
     rbx            0x3a     58
     rdx            0x3a     58                       ------> count
     rdi            0xf5a000ae     4120903854         ------> from
     rsp            0x7fbc3c7724c8   0x7fbc3c7724c8
     r9             0x3a     58
     r11            0x7fbc251cffb8   140446053236664
     r13            0x7fbc3c772708   140446445020936
     r15            0x7fbc3400b000   140446303039488
     eflags         0x202    [ IF ]
     ss             0x2b     43
     es             0x0    0
     gs             0x0    0
        
        
    (gdb) disassemble
    Dump of assembler code for function _Copy_conjoint_jshorts_atomic:
    => 0x00007fbc3afd3ef0 <+0>: mov    %rdx,%r8
       0x00007fbc3afd3ef3 <+3>: shr    $0x2,%rdx
       0x00007fbc3afd3ef7 <+7>: cmp    %rdi,%rsi
       0x00007fbc3afd3efa <+10>:    lea    -0x2(%rdi,%r8,2),%rax
       0x00007fbc3afd3eff <+15>:    jbe    0x7fbc3afd3f06 <acs_CopyRight>
       0x00007fbc3afd3f01 <+17>:    cmp    %rax,%rsi
       0x00007fbc3afd3f04 <+20>:    jbe    0x7fbc3afd3f84 <acs_CopyLeft>
    End of assembler dump.
    

    步骤分析:

    汇编指令说明rdi(from)rsi(to)rdx(count)r8raxeflags
    0xf5a000ae0xf5a02f00580xf5a000ae
    mov %rdx,%r80xf5a000ae0xf5a02f0058580xf5a000ae0x202 [ IF ]
    shr $0x2,%rdxrdx右移2位0xf5a000ae0xf5a02f0014580xf5a000ae0x203 [ CF IF ]
    cmp %rdi,%rsi比较两个指针位置0xf5a000ae0xf5a02f0014580xf5a000ae0x203 [ CF IF ]
    lea -0x2(%rdi,%r8,2),%raxrdi-2+(r8)*20xf5a000ae0xf5a02f0014580xf5a001200x203 [ CF IF ]
    jbe 0x7fbc3afd3f06 <acs_CopyRight>(CF=1 OR ZF=1跳转)to在from的更高内存位,从右到做复制,避免覆盖0xf5a000ae0xf5a02f0014580xf5a001200x203 [ CF IF ]
    cmp %rax,%rsi0xf5a000ae0xf5a02f0014580xf5a001200x202 [ IF ]
    jbe 0x7fbc3afd3f84 <acs_CopyLeft>0xf5a000ae0xf5a02f0014580xf5a001200x202 [ IF ]

    这里分acs_CopyRight和acs_CopyLeft是在内存拷贝时都要考虑的问题,保证复制目标可以顺利完成,不保证src一定不被覆盖。

    例如:0x0000 保存6个字节拷贝到 0x0004的位置:

    0000  0001  0002  0003  0004  0005  0006  0007  0008  0009  0010
       a     b     c     d     e     f     g     
                               |     |     |
    copy                       |     |     | 
                               |     |     |
                               a     b     c     d     e     f     g 
                               ( 被  覆  盖 )
    

    如果从左到右先拷贝a就会破坏原数据e、f、g,所以在拷贝需要从右到做复制。

    acs_CopyRight函数实现了具体的拷贝动作,基本都是寄存器mov操作,有兴趣可以看下hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.s:171。

    汇编运算速查:https://en.m.wikibooks.org/wiki/X86_Assembly/GAS_Syntax
    movl -8(%ebp,%edx,4),%eax # Full example: load *(ebp + (edx * 4) - 8) into eax
    movl -4(%ebp),%eax # Typical example: load a stack variable into eax
    movl (%ecx),%edx # No index: copy the target of a pointer into a register
    leal 8(,%eax,4),%eax # Arithmetic: multiply eax by 4 and add 8
    leal (%edx,%eax,2),%eax # Arithmetic: multiply eax by 2 and add edx

    总结下:Linux上OPENJDK使用汇编直接操作内存、寄存器来实现内存拷贝,避免了冗余的调用关系。数组拷贝可以尽量使用System.arraycopy发挥JDK极限性能。

    3 序列化

    ArrayList的序列化是比较有意思的,数据存储的变量被修饰transient,理论上直接序列化是不会编码这个变量的

    transientObject[] elementData; // non-private to simplify nested class access

    代码里面在开始序列化时,ObjectOutputStream会回调writeObject函数写数据(注意这个函数是没有继承关系的,直接按名字回调过来)

     private void writeObject(java.io.ObjectOutputStream s)
            throws java.io.IOException{
            // Write out element count, and any hidden stuff
            int expectedModCount = modCount;
            s.defaultWriteObject();
    
            // Write out size as capacity for behavioural compatibility with clone()
            s.writeInt(size);elementData更像是ArrayList的缓冲区,空间往往比实际使用大,所以使用这种方式序列化避免空间浪费。
    
    
    
            // Write out all elements in the proper order.
            for (int i=0; i<size; i++) {
                s.writeObject(elementData[i]);
            }
    
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
        }
    
     /**
         * The array buffer into which the elements of the ArrayList are stored.
         * The capacity of the ArrayList is the length of this array buffer. Any
         * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
         * will be expanded to DEFAULT_CAPACITY when the first element is added.
         */
        transient Object[] elementData; // non-private to simplify nested class access
    
    展开全文
  • java.lang.System的静态方法arraycopy()可以实现数组的复制,讲课的老师说这个方法效率比较高,如果数组有成千上万个元素,那么用这个方法,比用for语句循环快不少。于是我试了试,发现以下问题。如果是复制一个一位...

    java.lang.System的静态方法arraycopy()可以实现数组的复制,讲课的老师说这个方法效率比较高,如果数组有成千上万个元素,那么用这个方法,比用for语句循环快不少。于是我试了试,发现以下问题。

    如果是复制一个一位数组,那么改变复制后的数组并不影响原数组。但是如果复制一个二维数组,那么改变其中任何一个数组,那么另一个的值也发生了变化。开始不是很明白,后来上网查了查资料,理解了其中奥妙。

    java其实没有二维数组的概念,平常实现的二维数组只是元素是一维数组的一维数组,而数组也是引用类型,继承自Object类。数组是new出来的。这些性质也就导致arraycopy()二维数组时出现的问题。

    如果是一维数组,那么元素都是基础类型(如int,double等),使用arraycopy()方法后,是把原数组的值传给了新数组,属于值传递。而如果是二维数组,数组的第一维装的是一个一维数组的引用,第二维里是元素数值。对二维数组应用arraycopy()方法后,第一维的引用被复制给新数组的第一维,也就是两个数组的第一维都指向相同的“那些数组”。而这时改变其中任何一个数组的元素的值,其实都修改了“那些数组”的元素的值,所以原数组和新数组的元素值都一样了。

    OK,就是这样。

    不明白可以看看这个例子:

    1 public classTestArrayCopy {2

    3 /**

    4 *@paramargs5 */

    6 public static voidmain(String[] args) {7 //TODO 自动生成方法存根

    8

    9 String[] s1 = {"中国","山西","太原","TYUT","zyy","加拿大","不知道哪个州","不知道哪个市","不知道哪个学校","yxf"};10 String[] s2 = new String[10];11 System.arraycopy(s1, 0, s2, 0, 10);12 s2[6] = "假设蒙大拿州";13 s2[7] = "假设蒙特利尔市";14 s2[8] = "假设Montreal商学院";15

    16 System.out.println("This is s1");17 for(int i = 0;i < s1.length ;i++){18 System.out.print(s1[i] + ",");19 }20

    21 System.out.println("\nThis is s2");22 for(int i = 0;i < s2.length ;i++){23 System.out.print(s2[i] + ",");24 }25

    26 String[][] s3 = {{"中国","山西","太原","TYUT","zyy"},{"加拿大","不知道哪个州","不知道哪个市","不知道哪个学校","yxf"}};27 String[][] s4 = new String[s3.length][s3[0].length];28 System.arraycopy(s3, 0, s4, 0, s3.length);29

    30 System.out.println("\nThis is original s3");31 for(int i = 0;i < s3.length ;i++){32 for(int j = 0; j< s3[0].length ;j++){33 System.out.print(s3[i][j] + ",");34 }35 }36

    37 s4[1][1] = "假设蒙大拿州";38 s4[1][2] = "假设蒙特利尔市";39 s4[1][3] = "假设Montreal商学院";40

    41 System.out.println("\nThis is s3 after s4 has changed.");42 for(int i = 0;i < s3.length ;i++){43 for(int j = 0; j< s3[0].length ;j++){44 System.out.print(s3[i][j] + ",");45 }46 }47

    48 System.out.println("\nThis is s4");49 for(int i = 0;i < s4.length ;i++){50 for(int j = 0; j < s4[0].length ; j++){51 System.out.print(s4[i][j] + ",");52 }53

    54 }55 }56

    57 }

    结果:

    This is s1

    中国,山西,太原,TYUT,zyy,加拿大,不知道哪个州,不知道哪个市,不知道哪个学校,yxf,

    This is s2

    中国,山西,太原,TYUT,zyy,加拿大,假设蒙大拿州,假设蒙特利尔市,假设Montreal商学院,yxf,

    This is original s3

    中国,山西,太原,TYUT,zyy,加拿大,不知道哪个州,不知道哪个市,不知道哪个学校,yxf,

    This is s3 after s4 has changed.

    中国,山西,太原,TYUT,zyy,加拿大,假设蒙大拿州,假设蒙特利尔市,假设Montreal商学院,yxf,

    This is s4

    中国,山西,太原,TYUT,zyy,加拿大,假设蒙大拿州,假设蒙特利尔市,假设Montreal商学院,yxf,

    arraycopy的实现方法:

    其中 Arrays.copy是JDK1.6中引用的新方法。它调用了System.arraycopy完成相关数组的复制。

    在JDK1.6中ArrayList的相关add remove等操作都是调用System.arraycopy来对其底层的Object[]elementData数组进行操作的。

    LinkedList则使用一个Entry的内部类,其有指向next和previous的引用保存元素,它的遍历则先计算出所需index和size>>1(以为后的大小),确定是通过previous还是next遍历。

    System.arraycopy

    01.public static void arraycopy(Object src,

    02.                             int srcPos,

    03.                             Object dest,

    04.                             int destPos,

    05.                             int length)

    06.    从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。从 src 引用的源数组到 dest 引用的目标数组,数组组件的一个子序列被复制下来。被复制的组件的编号等于 length 参数。源数组中位置在 srcPos 到 srcPos+length-1 之间的组件被分别复制到目标数组中的 destPos 到 destPos+length-1 位置。

    它是个native方法,测试结果表明,

    当数组很小,但存是调用次数多的话。

    使用它复制数组并不比for循环手工复制数组快。

    但是如果是数组比较大,那么使用System.arraycopy会比较有优势,因为其使用的是内存复制,省去了大量的数组寻址访问等时间。

    native方法:

    Java不是完美的,Java的不足除了体现在运行速度上要比传统的C++慢许多之外,Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。

    可以将native方法比作Java程序同C程序的接口,其实现步骤:

    1、在Java中声明native()方法,然后编译;

    2、用javah产生一个.h文件;

    3、写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK带的jni.h文件);

    4、将第三步的.cpp文件编译成动态链接库文件;

    5、在Java中用System.loadLibrary()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。

    展开全文
  • 深入理解System.arraycopy内部原理       以前面试的时候总会被人问起一些Java里面的很多的东西,比如说ArrayList和Vector内部是如何实现当时我心里就一万个的草泥马,平时我们都不是...
  • System提供了一个静态方法arraycopy(),我们可以使用它来实现数组之间的复制。其函数原型是:public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)src:源数组;...
  • System提供了一个静态方法arraycopy(),我们可以使用它来实现数组之间的复制。其函数原型是:/*** Copies an array from the specified source array, beginning at the* specified position, to the specified ...
  • System.arraycopy是 ArrayList、StringBuilder、StringBuffer、Vector 底层实现 数组扩容的 最最最 重要的 调用的 API,它们的 数组扩容以及数组拷贝 完全依赖 于 System.arraycopy 这个API!。 Java提供的 System....
  • System.arraycopy方法解释

    2021-04-28 03:09:56
    */ public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length);** 例子如下: package test.demo; public class ArrayCopyTest { public static void main(String[] args)...
  • // [2, 3, 1, 0, 0, 0] // 此功能要求 // 源的起始位置+长度不能超过末尾 // 目标起始位置+长度不能超过末尾 // 且所有的参数不能为负数 try { System.arraycopy(ids, 0, ids2, 0, ids.length + 1); } catch ...
  • 1、说明System.arraycopy方法:如果是数组比较大,那么使用System.arraycopy会比较有优势,因为其使用的是内存复制,省去了大量的数组寻址访问等时间public static native void arraycopy(Object src, int srcPos,...
  • System.arraycopy 是一个 native 方法:public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length);而 native 方法和线程安全之间又没有什么必然联系, 并且我看 System....
  • 本文转载,原文链接:3分钟了解Java中System.arraycopy的用法 - 伊万夫斯基 - 博客园 https://www.cnblogs.com/benjieqiang/p/11428832.html3分钟了解Java中System.arraycopy的用法System提供了一个静态方法...
  • Arrays之arrayCopy讲解

    2021-06-07 15:29:15
    /* * Object:是所有引用数据类型的根父类 ...* public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length); * 第一个参数:src 原数组对象 * 第二个参数:srcPos ...
  • System.arraycopy 是个本地库函数 /** * src 数组源 * srcPos 数组起始位置 * dest 目标数据(最终想得到的数据存放的数组) * destPos 启始位置 * length 需要取的数组元个数 */ System.arraycopy(src, ...
  • Java System arraycopy()方法java.lang.System.arraycopy()方法复制从指定源数组的数组,开始在指定的位置,到目标数组的指定位置。阵列组件的子序列是从src引用由dest引用的目标数组源阵列复制。复制数组的数量等于...
  • 调用系统自带方法(System.arraycopy) 参考程序 @org.junit.Test public void fun(){ //创建一个存储被拷贝的字节数组,长度一定要比被拷贝字节数组大 byte[] bytes = new byte[20]; //创建一个需要字节拷贝...
  • System.arrayCopy的源代码声明 : public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 代码解释:  Object src : 原数组  int srcPos : 原数组的起始位置开始  Object ...
  • Is Java's System.arraycopy() efficient for small arrays, or does the fact that it's a native method make it likely to be substantially less efficient than a simple loop and a function call?Do native m...
  • Java中arraycopy方法的使用其源码简单解析实例应用 其源码 public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); 简单解析 从源码中可知调用arraycopy()方法...
  • arraycopy(要复制的数组,复制数组的起始位置,目标数组,复制的元素个数); 例子: int[] a = {1,2,3,4,5,6}; int[] b = new int[4]; //将数组a从第2个索引位置开始,截取4个元素到数组b从索引为0开始的位置添加...
  • public static native void arraycopy(Object src,int srcPos,Object dest,int destPos,int length); 语法中有五个参数,在调用时需要知道, 第一个参数src代表要复制的数组,即源数组。 第二个参数srcPos代表要...
  • 首先观察先System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)的声明: public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);...
  • System.arraycopy()

    2021-08-03 15:27:42
    System.arraycopy(nums, 0, numsSorted, 0, nums.length);
  • 3.数组复制arraycopy

    2021-01-24 01:17:02
    3.数组复制arrayCopy public static void arraycopy(Object src,int srcPos,Object dest,int desPos,int length): 数组中指定数据拷贝到另一个数组。 参数: src:源数组 srcPos:源数组中的起始位置 dest:目标...
  • java中的复制数组arraycopy(复制数组,从哪个元素开始复制,目标数组,复制到目标数组的其实下标,复制元素的个数) 【eg】System.arraycopy(aaaa,1,bbbb,0,3); public static void main(String[] args) { System....
  • 【Java集合 6】arraycopy方法的作用

    千次阅读 多人点赞 2021-01-26 19:39:50
    System.arraycopy arrays.copyof 下面分析一下最常用的System.arraycopy() 二、源码拜读 三、深拷贝与浅拷贝 1、简单类型(深拷贝) package com.guor.test.javaSE.collection; import java.util.Arrays; ...
  • System.arraycopy getting ava.lang.ArrayIndexOutOfBoundsException.. I am trying to copy data from one array to the next. but I am getting a exceptionprivate String array[] = { "NO DATA YET" };private.....
  • System.arraycopy() 方法

    2021-06-01 09:54:11
    System.arraycopy()是java的一个原生方法,用于数组之间的复制,当然延伸功能完成数组替换。 System.arraycopy(a,0,b,0,10) 从数组a中的第0个位置开始复制10个数,复制到b中,从b中的0位置开始存放 System.arraycopy...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 77,307
精华内容 30,922
关键字:

arraycopy