我应该如何在循环内创建对象而不浪费内存? [英] How should I create objecs inside a loop whithout wasting memory?

查看:132
本文介绍了我应该如何在循环内创建对象而不浪费内存?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经完成了一个测试Java程序,以了解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 =内存 - 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(
同时分配的最大对象数:+ maxObjects);
System.out.println(Max memory:+ maxMemory / 1024/1024 +MB);
System.out.println(最大使用内存:+ maxUsedMemory / 1024/1024 +MB);
System.out.println(最大空闲内存:+ maxFreeMemory / 1024/1024 +MB);
System.out.println(Total Time:+(t1 - t0)/ 60 +secconds);


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

 同时分配的最大对象数量:0 
最大内存:123MB
最大使用内存: 0MB
最大可用内存:122MB
总计时间:17秒

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

 同时分配的最大对象数量:8196834 
最大内存:696MB
最大使用内存: 485MB
最大可用内存:343MB
总计时间:163秒

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

 同时分配的最大对象数量:6 
最大内存:123MB
最大使用内存:0MB
最大可用内存:122MB
总时间:974秒

我相信这是不可能的,所以,这应该如何增加?有什么方法可以防止这种情况发生?



更新:

-Xmx64M:

 线程main中的异常java.lang.OutOfMemoryError:测试时超出
的GC开销限制。 Test.main(test.java:31)

结果与128MB相同。



测试2 whith -Xmx256M:

 (工作...)(超过21分钟...)


解决方案

编辑取出所有代码只是使JITs的工作变得更加困难,并且使用详细说明: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);




打印


$ b (分配失败)1535K-> 424K(7680K),0.0013697秒]
[GC(分配失败)1960K-> 384K(7680K)] $ b

  ,0.0013561秒] 
总时间:0.008秒

注意:这是迭代次数的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 对象在代码预热后全部放入堆栈而不是堆中,因此在这之后没有垃圾(除最后一行之外)。


I have done a test Java program to see how Java behave when "new" is used inside a loop, and my results are really bad. This is the program:

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

Test 1: "System.gc(); and test = new Test();" commented:

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

Test 2: "System.gc();" commented:

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

Test 3: nothing commented, total iterations lowered to 10000 (from 10000000):

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

I believe this is awfull, so, how this should be addresed? Is there any way to prevent this?

UPDATE:

Test 2 whith -Xmx64M:

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

Same result with 128MB.

Test 2 whith -Xmx256M:

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

解决方案

EDIT Taking out all the code which just makes the JITs job harder and running with verbose:gc -Xmx8m the following program

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

prints

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

Note: this is 100x the iteration count.

How should I create objecs inside a loop whithout wasting memory?

You are not just wasting memory you are wasting work. Note: System.gc() is many orders of magnitude more expensive than creating an object.

If you want to optimise you loop, create the object outside the loop. However, 99% of cases, you don't need to do this and in fact the JIT has Escape Analysis which places the fields of the object on the stack and eliminates the object entirely.

Try running this with -verbose:gc -Xmx32m it creates enough objects to fill the whole heap 1000x and yet

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

prints

2000000000

i.e. 2 billion Integer object but not enough garbage to trip even one collection. How is that possible? The Integer objects were all placed on the stack instead of the heap once the code warmed up so there was no garbage after this (except the last line)

这篇关于我应该如何在循环内创建对象而不浪费内存?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆