-
Bug
-
Resolution: Unresolved
-
P4
-
None
-
21, 25
-
generic
-
generic
A DESCRIPTION OF THE PROBLEM :
AutoShutdownDelegatedExecutorService is an Executor service that is auto-created when the following is called:
Executors.newSingleThreadExecutor(threadFactory);
Additional to delegation it also creates a phantom cleanable reference.
It correctly delegates shutdown() method and deregisters the cleanable BUT when shutdownNow() is called only delegation happens without cleanable deregistration.
This causes issues when used along with dynamic classloading. The cleanable reference remains there for a very long time and if it refers to an object loaded by already closed classloader it causes that classloader to hang in non-heap space for a very long time causing hogging of non-heap memory.
Suggested fix is to simply override shutdownNow() in AutoShutdownDelegatedExecutorService as follows:
@Override
public void shutdownNow() {
super.shutdownNow();
cleanable.clean(); // unregisters the cleanable
}
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a custom classloader and load a custom implementation of ThreadFactory
2. Call Executors.newSingleThreadExecutor(ThreadFactory threadFactory) ->providing the threadFactory from step #1
3. let the execution happen - submit something to the resulting executorservice
4. call shutdownNow() on the executorservice
5. close your custom classloader from step #1 (the one that loaded your threadFactory)
6. Dump heap & analyze - you should be able to see that your classloader will be long-lived and not GC-ed (because there is a phantom reference being held)
AutoShutdownDelegatedExecutorService is an Executor service that is auto-created when the following is called:
Executors.newSingleThreadExecutor(threadFactory);
Additional to delegation it also creates a phantom cleanable reference.
It correctly delegates shutdown() method and deregisters the cleanable BUT when shutdownNow() is called only delegation happens without cleanable deregistration.
This causes issues when used along with dynamic classloading. The cleanable reference remains there for a very long time and if it refers to an object loaded by already closed classloader it causes that classloader to hang in non-heap space for a very long time causing hogging of non-heap memory.
Suggested fix is to simply override shutdownNow() in AutoShutdownDelegatedExecutorService as follows:
@Override
public void shutdownNow() {
super.shutdownNow();
cleanable.clean(); // unregisters the cleanable
}
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a custom classloader and load a custom implementation of ThreadFactory
2. Call Executors.newSingleThreadExecutor(ThreadFactory threadFactory) ->providing the threadFactory from step #1
3. let the execution happen - submit something to the resulting executorservice
4. call shutdownNow() on the executorservice
5. close your custom classloader from step #1 (the one that loaded your threadFactory)
6. Dump heap & analyze - you should be able to see that your classloader will be long-lived and not GC-ed (because there is a phantom reference being held)
- duplicates
-
JDK-8362123 ClassLoader Leak via Executors.newSingleThreadExecutor(...)
-
- Open
-