Josu Goñi
Josu Goñi

Reputation: 1274

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

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...)

Upvotes: 0

Views: 193

Answers (2)

Peter Lawrey
Peter Lawrey

Reputation: 533492

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)

Upvotes: 3

Simone C.
Simone C.

Reputation: 369

If you create an Object, then some space is allocated. If you create 1000 Objects, then 1000 * x space is allocated.

There is no way to minimize the space of an Object, the solution is to create less Objects or wait for the Garbage Collection.

Upvotes: 3

Related Questions