Donald
Donald

Reputation: 127

Java out of memory errors

Why does this following code

List<Object> list = new ArrayList<>();
while (true) {
    for(int i = 0; i < 1000000; i++){
        list.add(new Object());
    }
}

produce an out of memory error

But this code doesn't

while(true) {
    List<Object> list = new ArrayList<>();
    for(int i = 0; i < 1000000; i++){
        list.add(new Object());
    }
}

I can see that it has something to do with the list being created either inside the while loop or outside of it obviously, but I am unsure on the reason why this happens.

Upvotes: 7

Views: 1413

Answers (8)

GentleCoder
GentleCoder

Reputation: 96

The only difference between the two codes is the location of List list = new ArrayList<>(); line. For the first code, ArrayList declared outside of the while loop and it keeps adding infinite numbers of objects into the one ArrayList instance so the out of memory occurs. On the other hand, the second one declares ArrayList inside of while loop so it will instantiate a new ArrayList after the each loop cycle(many ArrayList instances). By the rule of Garbage Collector in Java, the previous cycle's instances will be removed since it is no longer pointed. As a result, the GC in Java prevents the out of memory in the second case.

Upvotes: 2

user7627726
user7627726

Reputation:

In the first example, you create a list, add items to it and then the loop finishes. In the second example, you create a list, add things to it, then create a new List, add a bunch of things to it and repeat infinitely. Since in the first example your variable is created outside the loop, there is only 1 list to fill.

Upvotes: 2

hagrawal7777
hagrawal7777

Reputation: 14668

This question is well answered by @Eran, @TheLostMind and all, so I am not putting same point, I just want to take the opportunity make a point on how SoftReference and WeakReference helps to "delay" the out of memory exception.

Run below code with JVM arguments as -Xms64m -Xmx64m so that you can see the results quickly.

import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class OOM {
    public static void main(String[] args) {
        System.out.println(new Date());
        try {
            scenario1(false, false); // in my box, OOM occurred with average of 2 seconds.
            //scenario1(true, false); // in my box, OOM occurred average of 6 seconds.
            //scenario1(false, true); // in my box, OOM occurred average of 8 seconds.
        } catch (Exception e) {
        } catch (Error err){

        }
        System.out.println(new Date());
    }

    private static void scenario1(boolean useSoftReference, boolean useWeakReference) {
        List<Object> list = new ArrayList<>();
        while (true) {
            for(int i = 0; i < 1000000; i++){
                if(useSoftReference){
                    list.add(new SoftReference<Object>(new Object()));
                } else if(useWeakReference){
                    list.add(new WeakReference<Object>(new Object()));
                } else{
                    list.add(new Object());
                }
            }
        }
    }
}

Upvotes: 3

Saurabh Jhunjhunwala
Saurabh Jhunjhunwala

Reputation: 2922

In the first scenario, the list object is declared outside the while loop, which again is running indefinitely ( as while (true )), thus it keeps on adding till it runs out of memory, while in the second one because you have declared the list inside the while, the max size is restricted to the number of iterations of the for loop.

Each time the for loop exists, the list object is reset that is new one is created to which you start adding, thus you have an upper limit. The old object is garbage collected thus clearing the JVM.

Upvotes: 3

Eran
Eran

Reputation: 394066

In the first case, you have a single ArrayList instance and you keep adding to it new Object instances until you run out of memory.

In the second case, you create a new ArrayList in each iteration of the while loop and add 1000000 Object instances to it, which means the ArrayList created in the previous iteration and the 1000000 Object instances it contains can be garbage collected, since the program no longer has references to them.

Note that the second snippet can also cause out of memory error if the new Objects are created faster than the garbage collector can release the old ones, but that depends on the JVM implementation.

Upvotes: 10

Spitzbueb
Spitzbueb

Reputation: 5911

Because when you create the list inside the while loop your previous list gets dumped and you have a new empty list. Afterwards your memory gets freed by the java garbage collector and you add 1000000 elements to the list. Then a new list gets created and everything repeats itself.

Upvotes: 3

TheLostMind
TheLostMind

Reputation: 36304

In your second case the list created (and where the elements are added) is going out of scope and eligible for GC. This will be GCed to make memory for the new ArrayList. Thus for every iteration, a new ArrayList is created and then it becomes eligible for GC (when the loop ends). Thus when memory is low, these objects will be GCed.

In the first case you are adding elements to the same ArrayList. Nothing is being GCed.

Upvotes: 4

Mureinik
Mureinik

Reputation: 312126

In the first snippet, the list is created (and retained!) outside the loop, so you just keep endlessly adding elements to it until you consume all the available memory.

In the second snippet, each iteration of the while loop creates a new ArrayList object. Since you no longer hold a reference to that instance once the iteration ends, this list is eligible for garbage collection, so the old lists keep getting freed and you don't exhaust your memory.

Upvotes: 5

Related Questions