如何在循环中创建对象而不浪费内存?

时间:2016-07-03 14:59:58

标签: java garbage-collection

我已经完成了一个测试Java程序,看看在循环中使用“new”时Java的行为如何,我的结果非常糟糕。这是该计划:

package test;

public class Test {
    static int objectCount = 0;

    public static int getObjectCount() {
        return objectCount;
    }

    public Test() {
        objectCount++;
    }

    public void finalize() {
        objectCount--;
    }


    public static void main(String[] args) {
        int maxObjects = 0;
        long maxMemory = 0;
        long maxUsedMemory = 0;
        long maxFreeMemory = 0;
        long memory = 0;
        long usedMemory = 0;
        long freeMemory = 0;

        final long t0 = System.currentTimeMillis();
        Test test = null;
        for (int i=0; i<10000000; i++) {
            System.gc();
            test = new Test();

            memory = Runtime.getRuntime().totalMemory();
            freeMemory = Runtime.getRuntime().freeMemory();
            usedMemory = memory - freeMemory;

            if (maxMemory < memory) maxMemory = memory;
            if (maxFreeMemory < freeMemory) maxFreeMemory = freeMemory;
            if (maxUsedMemory < usedMemory) maxUsedMemory = usedMemory;

            if (maxObjects < getObjectCount()) maxObjects = getObjectCount();
        }
        final long t1 = System.currentTimeMillis(); 

        System.out.println(
            "Maximum number of objects simultaneously allocated: "+ maxObjects);
        System.out.println("Max memory: " + maxMemory/1024/1024 + "MB");
        System.out.println("Max used memory: " + maxUsedMemory/1024/1024 +"MB");
        System.out.println("Max free memory: " + maxFreeMemory/1024/1024 +"MB");
        System.out.println("Total Time: " + (t1 - t0)/60 + " secconds");
    }
}

测试1:“System.gc();和test = new Test();”评论:

Maximum number of objects simultaneously allocated: 0
Max memory: 123MB
Max used memory: 0MB
Max free memory: 122MB
Total Time: 17 secconds

测试2:“System.gc();”评论:

Maximum number of objects simultaneously allocated: 8196834
Max memory: 696MB
Max used memory: 485MB
Max free memory: 343MB
Total Time: 163 secconds

测试3:没有评论,总迭代次数降至10000(从10000000):

Maximum number of objects simultaneously allocated: 6
Max memory: 123MB
Max used memory: 0MB
Max free memory: 122MB
Total Time: 974 secconds

我相信这很糟糕,所以,应该如何添加?有什么方法可以阻止这种情况吗?

更新

测试2 whith -Xmx64M:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at test.Test.main(test.java:31)

与128MB相同的结果。

测试2 whith -Xmx256M:

(working...) (More than 21 minutes...)

2 个答案:

答案 0 :(得分:4)

编辑取出所有使JIT工作更加困难并且使用verbose:gc -Xmx8m以下程序运行的代码

public class Test {
    public static void main(String[] args) {
        final long t0 = System.currentTimeMillis();
        for (int i = 0; i < 100 * 10000000; i++) {
            Test test = new Test();
        }
        final long t1 = System.currentTimeMillis();
        System.out.println("Total Time: " + (t1 - t0) / 1000.0 + " secconds");
    }
}

打印

[GC (Allocation Failure)  1535K->424K(7680K), 0.0013697 secs]
[GC (Allocation Failure)  1960K->384K(7680K), 0.0013561 secs]
Total Time: 0.008 secconds

注意:这是迭代次数的100倍。

  

如何在不浪费内存的情况下在循环内创建对象?

你不只是在浪费记忆而浪费你的工作。注意:System.gc()比创建对象要贵许多个数量级。

如果要优化循环,请在循环外创建对象。但是,99%的情况下,您不需要这样做,实际上JIT具有Escape Analysis,它将对象的字段放在堆栈上并完全消除对象。

尝试使用-verbose:gc -Xmx32m运行它,它会创建足够的对象来填充整个堆1000x而且

public class EscapeAnalysisMain {
    public static void main(String[] args) {
        int i;
        for (i = 0; i < 2_000_000_000; i++) {
            Integer x = i;
            if (x.hashCode() < 0)
                throw new AssertionError();
        }
        System.out.println(i);
    }
}

打印

2000000000

即。 20亿Integer个对象但没有足够的垃圾来绊倒一个集合。怎么可能?一旦代码变热,Integer对象都被放置在堆栈而不是堆中,因此在此之后没有垃圾(除了最后一行)

答案 1 :(得分:3)

如果创建Object,则会分配一些空间。 如果创建1000个对象,则分配1000 * x空间。

无法最小化Object的空间,解决方案是创建更少的对象或等待垃圾收集。