user1191027
user1191027

Reputation:

ExecutorService causing out of memory exception after frequent shutdowns

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class ExecutorServiceTest {

    public static void main(String args[]) {
        new ExecutorServiceTest();
    }

    public ExecutorServiceTest() {
        while (true) {
            action();
        }
    }

    public String action() {
        String string = "";
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new Callable() {
            @Override
            public String call() {
                return randomString();
            }
        });
        try {
            string = future.get(1, TimeUnit.MILLISECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException ex) {

        }
        future.cancel(true);
        executor.shutdownNow();
        return string;
    }

    public String randomString() {
        return "asdhkashdkjshakdasdsahdka";
    }

}

If you open up a task manager window and then run this class, you will notice that Java instantly uses a ton of memory. I have searched and debugged the class thoroughly but cannot find the memory leak.

What is causing future.get(1, TimeUnit.MILLISECONDS); to use up all the system memory and then crash?

Upvotes: 2

Views: 3586

Answers (2)

Tagir Valeev
Tagir Valeev

Reputation: 100289

The ThreadPoolExecutor class has non-empty finalize() method (which performs the pool shutdown), so when garbage-collected it first goes to the finalizer queue (which is basically linked list) and processed by separate "Finalizer" thread. Even if the pool has no active tasks, shutting it down is quite slow. Depending on the hardware, OS and running conditions it's possible that new finalizers are added faster than processed by Finalizer thread, thus you may expect constant memory growth. In general you should not create and shutdown executors too fast.

Upvotes: 7

Danail Alexiev
Danail Alexiev

Reputation: 7782

I think the culprit is here:

while (true) {
    action();
}

This is spawning new threads aggressively (this is an expensive operation) and, IMHO, this leads to the memory issues and the crash.

Also, I think this is a bad example of using a single threaded executor. It is more suitable for serial execution of tasks in a single background thread. If you need to add a big number of new tasks, that have to be executed simultaneously, it's better to use a thread pool. Controlling the number of worker threads is going to prevent the program from running out of memory.

Working code:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class ExecutorServiceTest {

    ExecutorService executor = Executors.newSingleThreadExecutor();

    public static void main(String args[]) {
        new ExecutorServiceTest();
    }

    public ExecutorServiceTest() {
        while (true) {
            action();
        }
    }

    public String action() {
        String string = "";
        if (executor.isShutdown()) {
            Future<String> future = executor.submit(new Callable() {
                @Override
                public String call() {
                    return randomString();
                }
            });
            try {
                string = future.get(1, TimeUnit.MILLISECONDS);
            } catch (InterruptedException | ExecutionException | TimeoutException ex) {
                System.out.println(ex);
            }
            future.cancel(true);
            executor.shutdownNow();
        }
        return string;
    }

    public String randomString() {
        return "asdhkashdkjshakdasdsahdka";
    }

}

Upvotes: 0

Related Questions