System.arraycopy

当我还年幼的时候,我很任性,复制数组也是,写一个for循环,来回倒腾,后来长大了,就发现了System.arraycopy的好处。

为了测试俩者的区别我写了一个简单赋值int[100000]的程序来对比,并且中间使用了nanoTime来计算时间差:

程序如下:

        int[] a = new int[100000];
        for(int i=0;i<a.length;i++){
            a[i] = i;
        }

        int[] b = new int[100000];

        int[] c = new int[100000];
        for(int i=0;i<c.length;i++){
            c[i] = i;
        }

        int[] d = new int[100000];

        for(int k=0;k<10;k++){
            long start1 = System.nanoTime();
            for(int i=0;i<a.length;i++){
                b[i] = a[i];
            }
            long end1 = System.nanoTime();
            System.out.PRintln("end1 - start1 = "+(end1-start1));

            long start2 = System.nanoTime();
            System.arraycopy(c, 0, d, 0, 100000);
            long end2 = System.nanoTime();
            System.out.println("end2 - start2 = "+(end2-start2));

            System.out.println();
        }

为了避免内存不稳定干扰和运行的偶然性结果,我在一开始的时候把所有空间申明完成,并且只之后循环10次执行,得到如下结果:

end1 - start1 = 366806
end2 - start2 = 109154

end1 - start1 = 380529
end2 - start2 = 79849

end1 - start1 = 421422
end2 - start2 = 68769

end1 - start1 = 344463
end2 - start2 = 72020

end1 - start1 = 333174
end2 - start2 = 77277

end1 - start1 = 377335
end2 - start2 = 82285

end1 - start1 = 370608
end2 - start2 = 66937

end1 - start1 = 349067
end2 - start2 = 86532

end1 - start1 = 389974
end2 - start2 = 83362

end1 - start1 = 347937
end2 - start2 = 63638

可以看出,System.arraycopy的性能很不错,为了看看究竟这个底层是如何处理的,我找到openJDK的一些代码留恋了一些:

System.arraycopy是一个native函数,需要看native层的代码:

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

找到对应的openjdk6-src/hotspot/src/share/vm/prims/jvm.cpp,这里有JVM_ArrayCopy的入口:

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
  Klass::cast(s->klass())->copy_array(s, src_pos, d, dst_pos, length, thread);
JVM_END

前面的语句都是判断,知道最后的copy_array(s, src_pos, d, dst_pos, length, thread)是真正的copy,进一步看这里,在openjdk6-src/hotspot/src/share/vm/oops/typeArrayKlass.cpp中:

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);//还是在这里处理copy
}

这个函数之前的仍然是一堆判断,直到最后一句才是真实的拷贝语句。

在openjdk6-src/hotspot/src/share/vm/utilities/copy.cpp中找到对应的函数:

// 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;

  // (Note:  We could improve performance by ignoring the low bits of size,
  // and putting a short cleanup loop after each bulk copy loop.
  // There are plenty of other ways to make this faster also,
  // and it's a slippery slope.  For now, let's keep this code simple
  // since the simplicity helps clarify the atomicity semantics of
  // this Operation.  There are also CPU-specific assembly versions
  // which may or may not want to include such optimizations.)

  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);
  }
}

上面的代码展示了选择哪个copy函数,我们选择conjoint_jints_atomic,在openjdk6-src/hotspot/src/share/vm/utilities/copy.hpp进一步查看:

// jints,                 conjoint, atomic on each jint
  static void conjoint_jints_atomic(jint* from, jint* to, size_t count) {
    assert_params_ok(from, to, LogBytesPerInt);
    pd_conjoint_jints_atomic(from, to, count);
  }

继续向下查看,在openjdk6-src/hotspot/src/cpu/zero/vm/copy_zero.hpp中:

static void pd_conjoint_jints_atomic(jint* from, jint* to, size_t count) {
  _Copy_conjoint_jints_atomic(from, to, count);
}

继续向下查看,在openjdk6-src/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp中:

void _Copy_conjoint_jints_atomic(jint* from, jint* to, size_t count) {
    if (from > to) {
      jint *end = from + count;
      while (from < end)
        *(to++) = *(from++);
    }
    else if (from < to) {
      jint *end = from;
      from += count - 1;
      to   += count - 1;
      while (from >= end)
        *(to--) = *(from--);
    }
  }

可以看到,直接就是内存块赋值的逻辑了,这样避免很多引用来回倒腾的时间,必然就变快了。

时间: 2025-01-23 18:59:06

System.arraycopy的相关文章

新手求教: System.arrayCopy的用法

问题描述 能把System.arraycopy(students,0,studentCopy,0,students.length);这句话括号里面的意思详细的解释一下么谢谢啦 解决方案 解决方案二:从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束.从src引用的源数组到dest引用的目标数组,数组组件的一个子序列被复制下来.被复制的组件的编号等于length参数.源数组中位置在srcPos到srcPos+length-1之间的组件被分别复制到目标数组中的destPos到

java 数组的拷贝 对象数组的拷贝 System.arraycopy函数

  /*java 数组的操作   System类的arraycopy静态函数用于数组拷贝   数组是不能越界的 对象数组的复制实际上是复制的引用的拷贝  也就是对象的地址  */ class Point{ int x; int y; Point(int x,int y) {  this.x=x;  this.y=y; }}class  Test{ public static void main(String []args) {  /*int a[]=new int[]{1,2,3,4,5};  

Java基础-14总结正则表达式,Pattern,Mactcher,Math,BigInteger,BigDeximal,System等

你需要的是什么,直接评论留言. 获取更多资源加微信公众号"Java帮帮" (是公众号,不是微信好友哦) 还有"Java帮帮"今日头条号,技术文章与新闻,每日更新,欢迎阅读 学习交流请加Java帮帮交流QQ群553841695 分享是一种美德,分享更快乐! 学正则表达式之前qq号问题: package cn.itcast_01; import java.util.Scanner; /*  * 校验qq号码.  *  1:要求必须是5-15位数字  *  2:0不能开头

javase-关于system.in输入流 问题!

问题描述 关于system.in输入流 问题! 如图所示!如果是读取文件,当读到文件末尾条件等于null,退出循环..而这里如果不输入内容,那程序是在哪里阻塞着???为什么??它不是应该一直readline吗?? 解决方案 是这一句阻塞了. str=br.readLine(); 你输入完毕后,会进入第二次等待输入状态. 在BufferedReader中有个方法叫fill方法,会一直监听控制台输入,可以具体的看一下. 解决方案二: 你发的图看不清楚啊,分辨率太小了. 解决方案三: 在java.io

System.arraycopy--findbugs检查引发的更改

EI2: This code stores a reference to an externally mutable object into the internal representation of the object.  If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important p

java用arraycopy实现多击事件_java

本文实例为大家分享了java用arraycopy实现多击事件的3种方法,供大家参考,具体内容如下 1.双击事件的实现 我们规定两次点击的事件间隔在500毫秒内为双击事件,这一值可以随意限定. bt_click.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(startTime!=0){ long endTime = System.currentTimeMillis(); i

Java System类的使用

import java.util.*; public class SystemTest { public static void main(String[] args) { //测试arraycopy方法,注意,目的空间必须提前分配 int[] src = {1,2,3,4,5,6,7,8,9}; System.out.println("System.arraycopy"); int[] dst = new int[src.length]; System.arraycopy(src,

移除UTF-8文件头的BOM

inkfish原创,请勿商业性质转载,转载请注明来源(http://blog.csdn.net/inkfish ).(来源:http://blog.csdn.net/inkfish) UTF-8 BOM又叫UTF-8 签名,在UTF-8文件的头部,长度为3个字节.其实UTF-8 的BOM对UFT-8没有作用,BOM签名的意思就是告诉编辑器当前文件采用何种编码,方便编辑器识别.但是在Eclipse中,带有BOM的java源码生成javadoc时却会出现如下错误:(来源:http://blog.cs

[数据结构] 栈

栈(stack)又名堆栈,它是一种运算受限的线性表.其限制是仅允许在表的一端进行插入和删除运算.这一端被称为栈顶,相对地,把另一端称为栈底.向一个栈插入新元素又称作进栈.入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素:从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素. 栈的示意图: java中的Stack Stack 类表示后进先出(LIFO)的对象堆栈.它通过五个操作对类 Vector 进行了扩展 ,允许将向量视为堆栈.它提供了通常的 p