Register application shutdown hook lazily
Previously, SpringApplicationShutdownHook would always register a shutdown hook, even if SpringApplication was configured not to use a shutdown hook, such as in a war deployment. This could result in a memory leak when the war was undeployed. The shutdown hook registered by SpringApplicationShutdownHook would remain registered, pinning the web application's class loader in memory. This commit updates SpringApplicationShutdownHook so that it registers a shutdown hook with the JVM lazily, upon registeration of the first application context. Fixes gh-27987
This commit is contained in:
parent
afb81f14ea
commit
a4f1d32203
|
|
@ -24,6 +24,7 @@ import java.util.Set;
|
|||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
|
@ -59,26 +60,16 @@ class SpringApplicationShutdownHook implements Runnable {
|
|||
|
||||
private final ApplicationContextClosedListener contextCloseListener = new ApplicationContextClosedListener();
|
||||
|
||||
private final AtomicBoolean shutdownHookAdded = new AtomicBoolean(false);
|
||||
|
||||
private boolean inProgress;
|
||||
|
||||
SpringApplicationShutdownHook() {
|
||||
try {
|
||||
addRuntimeShutdownHook();
|
||||
}
|
||||
catch (AccessControlException ex) {
|
||||
// Not allowed in some environments
|
||||
}
|
||||
}
|
||||
|
||||
protected void addRuntimeShutdownHook() {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this, "SpringApplicationShutdownHook"));
|
||||
}
|
||||
|
||||
SpringApplicationShutdownHandlers getHandlers() {
|
||||
return this.handlers;
|
||||
}
|
||||
|
||||
void registerApplicationContext(ConfigurableApplicationContext context) {
|
||||
addRuntimeShutdownHookIfNecessary();
|
||||
synchronized (SpringApplicationShutdownHook.class) {
|
||||
assertNotInProgress();
|
||||
context.addApplicationListener(this.contextCloseListener);
|
||||
|
|
@ -86,6 +77,21 @@ class SpringApplicationShutdownHook implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
private void addRuntimeShutdownHookIfNecessary() {
|
||||
if (this.shutdownHookAdded.compareAndSet(false, true)) {
|
||||
addRuntimeShutdownHook();
|
||||
}
|
||||
}
|
||||
|
||||
void addRuntimeShutdownHook() {
|
||||
try {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this, "SpringApplicationShutdownHook"));
|
||||
}
|
||||
catch (AccessControlException ex) {
|
||||
// Not allowed in some environments
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Set<ConfigurableApplicationContext> contexts;
|
||||
|
|
|
|||
|
|
@ -46,8 +46,11 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
|||
class SpringApplicationShutdownHookTests {
|
||||
|
||||
@Test
|
||||
void createCallsRegister() {
|
||||
void shutdownHookIsNotAddedUntilContextIsRegistered() {
|
||||
TestSpringApplicationShutdownHook shutdownHook = new TestSpringApplicationShutdownHook();
|
||||
assertThat(shutdownHook.isRuntimeShutdownHookAdded()).isFalse();
|
||||
ConfigurableApplicationContext context = new GenericApplicationContext();
|
||||
shutdownHook.registerApplicationContext(context);
|
||||
assertThat(shutdownHook.isRuntimeShutdownHookAdded()).isTrue();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue