Java:制作初始ArrayList的本地副本的最快方法?

时间:2014-09-23 20:02:18

标签: java arrays arraylist

我的代码要求我在每次调用函数时创建一个大的(301x301x19项)ArrayList,它有一些初始值(有些是0,有些是1等)。起始值总是相同的,每次调用函数时都需要加载到数组中,以便函数有自己的这些初始值的副本来处理。

最初我每次调用函数时都在重新计算数组,但事实证明这是可笑的慢;相反,我现在只计算一次初始数组,并且每次调用函数时都会对它进行局部复制(这样我就可以在不改变初始数组值的情况下更改值)。

然而,复制数组仍然被证明是非常慢的(只需要花费超过3/4的计算时间来复制这个数组)。我尝试过以下方法:

// oldList is an ArrayList<Byte>
ArrayList<Byte> newList = new ArrayList<Byte>(oldList);

// oldList is an ArrayList<Byte>
ArrayList<Byte> newList = new ArrayList<Byte>();
newList.addAll(oldList);

// oldList is a Byte[]
ArrayList<Byte> newList = new ArrayList<Byte>(Arrays.asList(oldList));

所有这些方法对我的应用来说都太慢了;有什么更快的技术可以做到这一点还是我运气不好?

1 个答案:

答案 0 :(得分:4)

总结:

  1. 旨在设计复制这么多大型数据结构的需要(我知道这是一个难题)
  2. 避免指针追逐,使用数组而不是ArrayLists。如果对象包含其他对象,请尝试将其替换为基元。这里的最终目的是减少一个基元数组,例如字节数组
  3. 压缩数据结构,使用数组,更小的类型;目标是通过复制较少的实际字节获得相同的收益
  4. 使用System.arrayCopy
  5. 如果你仍然想要更快,那么将内存布局和责任从JVM中取出并直接使用sun.misc.Unsafe(也就是说&#39;用剪刀运行&#39;)
  6. 更改为更容易复制的数据结构,使用System.arraycopy的速度将与您在问题中概述的方法一样快。

    System.arraycopy作为本机调用实现。大多数JVM提供程序都准备了一个本机版本,它使用本机指令来加速内存复制。

    不幸的是,复制大面积的内存会在JVM中产生意想不到的副作用,主要是在垃圾收集器周围。

    1. 在mem copy期间,JVM无法访问安全点,这会阻止STW GC启动
    2. GC未启动导致确实注意安全点的线程等待更长时间,在与此工作无关的线程上创建奇怪的停顿
    3. 大型数组可能不适合TAB(用于加速对象分配的线程本地缓冲区),这意味着对象分配在进入特殊情况代码时会变慢
    4. 大型物体增加了GC周期中过早使用的可能性,这增加了更昂贵的老一代/完整GC的频率(与更便宜的年轻一代GC相对)

      • 注意:要看到上述效果,我们必须谈论非常高的分配率和丢弃率。大多数在这里和那里做一些分配和复制的算法都不会看到这些问题;现代JVM甚至可以应对相当高的速率,这些问题在超过阈值之前不会发生,而且我们在极点上旋转的印版开始发挥作用。