1

我试图通过填充超过百万行来计时 Arraylist 和 Linkedlist,并在 Arraylist 填充后得到以下错误,

线程“主”java.lang.OutOfMemoryError 中的异常:java.lang.Integer.valueOf(Integer.java:642) 处的 Java 堆空间在 scratch.Collectionss.main(Collections.java:25)

如何避免此错误,我尝试设置 l1 = null 但这给了我一个错误,

public class Collectionss {
    public static void main(String[] args){
        // 
        long starttime = System.currentTimeMillis();
        List<Integer> l1 = new ArrayList<Integer>();
        for (int i = 1; i <= 10000000; i++){
            l1.add(i);
        }
        System.out.println(l1.size());

        long endtime = System.currentTimeMillis();

        System.out.println(endtime - starttime);

        //
        long starttime1 = System.currentTimeMillis();
        List<Integer> l2 = new LinkedList<Integer>();
        for (int i = 1; i <= 10000000; i++){
            l2.add(i);
        }
        System.out.println(l2.size());

        long endtime1 = System.currentTimeMillis();

        System.out.println(endtime1 - starttime1);

    }

}
4

6 回答 6

6

您应该在不同的方法中运行这些测试,因为第一个循环的优化可能会干扰第二个循环的优化,即第二个循环可能会更慢,因为它是第二个。

我建议您至少运行两个测试 10(或 2 秒)并使用具有更高分辨率的 System.nanoTime()。

如果你这样做了,但内存仍然不足,我建议你增加最大内存大小。如果您运行的是 32 位 Windows,则默认值非常低。-Xmx1g您可以在命令行上增加它


如果您运行以下命令,您可以看到 GC 的影响最大,这并不奇怪,因为问题最多会产生垃圾

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class Collectionss {

    public static final int TO_ADD = 10000000;

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            long timeAL = timeAddToArrayList();
            long timeLL = timeAddsToLinkedList();
            System.out.printf("Time to add %,d Integer to ArrayList %.3f sec, LinkedList %.3f%n",
                    TO_ADD, timeAL / 1e9, timeLL / 1e9);
        }
    }

    private static long timeAddToArrayList() {
        long starttime = System.nanoTime();
        List<Integer> l1 = new ArrayList<Integer>();
        for (int i = 1; i <= TO_ADD; i++) {
            l1.add(i);
        }
        assert TO_ADD == l1.size();

        return System.nanoTime() - starttime;
    }

    private static long timeAddsToLinkedList() {
        long starttime = System.nanoTime();
        List<Integer> l2 = new LinkedList<Integer>();
        for (int i = 1; i <= TO_ADD; i++) {
            l2.add(i);
        }
        assert TO_ADD == l2.size();

        return System.nanoTime() - starttime;
    }
}

印刷

Time to add 10,000,000 Integer to ArrayList 0.238 sec, LinkedList 1.326
Time to add 10,000,000 Integer to ArrayList 1.193 sec, LinkedList 0.971
Time to add 10,000,000 Integer to ArrayList 0.841 sec, LinkedList 0.048
Time to add 10,000,000 Integer to ArrayList 0.349 sec, LinkedList 1.128
Time to add 10,000,000 Integer to ArrayList 0.064 sec, LinkedList 0.048

但是在每次测试之前添加 System.gc() ,你会得到

Time to add 10,000,000 Integer to ArrayList 0.241 sec, LinkedList 2.130
Time to add 10,000,000 Integer to ArrayList 0.070 sec, LinkedList 0.072
Time to add 10,000,000 Integer to ArrayList 0.067 sec, LinkedList 0.053
Time to add 10,000,000 Integer to ArrayList 0.069 sec, LinkedList 0.048
Time to add 10,000,000 Integer to ArrayList 0.065 sec, LinkedList 0.051
于 2013-09-10T15:35:36.560 回答
3

如果您正在处理大型数据集,那么ArrayList在您的情况下,使用正确的大小初始化您总是更好。

List<Integer> l1 = new ArrayList<Integer>(10000000);,否则你ArrayList的默认大小将是10,并且每次超过大小时,该add方法都会创建一个新array的大小增加的内容,复制旧的array内容arraylist

请参阅下面的ArrayList ensureCapacity方法来源。

if (minCapacity > oldCapacity) {
    Object oldData[] = elementData;
    int newCapacity = (oldCapacity * 3)/2 + 1;
        if (newCapacity < minCapacity)
    newCapacity = minCapacity;
        // minCapacity is usually close to size, so this is a win:
        **elementData = Arrays.copyOf(elementData, newCapacity);**
}

如果它仍然发生,即使在初始化之后,也会按照帖子中其他人的建议增加你的堆大小。

于 2013-09-10T15:46:32.390 回答
2

尝试增加你的堆大小并运行你的程序

于 2013-09-10T15:33:22.067 回答
1

-Xmx4g您需要通过标志增加 JVM 可用的堆。这会将堆大小增加到 4GB。你不需要命令行。IDE 也接受 JVM 参数。例如。蚀:Run Configurations... -> Arguments -> VM Arguments

正如@crush 提到的,如果您 ArrayList使用所需的容量进行初始化,您可以节省内存(特别是 GC 一些工作)。

于 2013-09-10T15:33:17.527 回答
1

我认为您可以在运行程序时指定堆大小。如果您在命令行上执行,每当您使用“java”执行时,请包含一个参数:“-Xms1024m -Xmx1024m”或任何您想要的堆大小。查看 jvm 启动参数以获取确切的用法。

于 2013-09-10T15:34:22.457 回答
0

您正在列表集合中添加 10000000 个项目。每次肯定会增加内存消耗并最终跨越堆大小。

而且您正在创建这样的集合两次。

你需要弄清楚为什么你需要这么大的收藏。如果您需要如此大的集合,那么您需要增加所需的堆空间。

于 2013-09-10T15:31:44.133 回答