Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OutOfMemoryError - why can a waiting Thread not be garbage collected?

This simple sample code demonstrates the problem. I create an ArrayBlockingQueue, and a thread that waits for data on this queue using take(). After the loop is over, in theory both the queue and the thread can be garbage collected, but in practice I soon get an OutOfMemoryError. What is preventing this to be GC'd, and how can this be fixed?

/**
 * Produces out of memory exception because the thread cannot be garbage
 * collected.
 */
@Test
public void checkLeak() {
    int count = 0;
    while (true) {

        // just a simple demo, not useful code.
        final ArrayBlockingQueue<Integer> abq = new ArrayBlockingQueue<Integer>(2);
        final Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    abq.take();
                } catch (final InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();

        // perform a GC once in a while
        if (++count % 1000 == 0) {
            System.out.println("gc");
            // this should remove all the previously created queues and threads
            // but it does not
            System.gc();
        }
    }
}

I am using Java 1.6.0.

UPDATE: perform GC after a few iterations, but this does not help.

like image 363
martinus Avatar asked Apr 24 '09 08:04

martinus


3 Answers

Threads are top level objects. They are 'special' so they do not follow the same rules as other objects. The do not rely on references to keep them 'alive' (i.e. safe from GC). A thread will not get garbage collected until it has ended. Which doesn't happen in your sample code, since the thread is blocked. Of course, now that the thread object is not garbage collected, then any other object referenced by it (the queue in your case) also cannot be garbage collected.

like image 174
Robin Avatar answered Nov 04 '22 20:11

Robin


You are creating threads indefinitely because they all block until ArrayBlockingQueue<Integer> abq has some entry. So eventually you'll get a OutOfMemoryError.

(edit)

Each thread you create will never end because it blocks until the abq queue as one entry. If the thread is running, the GC isn't going to collect any object that the thread is referencing including the queue abq and the thread itself.

like image 34
bruno conde Avatar answered Nov 04 '22 18:11

bruno conde


abq.put(0);

should save your day.

Your threads all wait on their queue's take() but you never put anything in those queues.

like image 31
cadrian Avatar answered Nov 04 '22 19:11

cadrian