观察org.apache.catalina.startup.Catalina中的start()方法,我们发现Serever启动后,还要执行如下代码:
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
if (await) {
await();
stop();
}
加粗部分代码的作用是在jvm中增加一个关闭钩子,当jvm关闭的时候,会先执行系统中通过addShutdownHook()添加的钩子,当系统执行完这些钩子后,jvm才会关闭。这些钩子会在jvm关闭时进行对象销毁、内存清理等操作。
shutdownHook的意义如下:
1.应用程序正常退出,在退出时执行特定的业务逻辑,或者关闭资源等操作。
public class JVMHook {
public static void start() {
System.out.println("The JVM is started");
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
System.out.println("The JVM Hook is execute");
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public static void main(String[] args) {
start();
System.out.println("The Application is doing something");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
如上模拟了正常退出的场景,程序执行结果是”
The JVM is started
The Application is doing something
The JVM Hook is execute
“
我们再来看一下Tomcat服务器shutdown时执行的stop()方法,
/**
* Stop an existing server instance.
*/
public void stop() {
try {
// Remove the ShutdownHook first so that server.stop()
// doesn't get invoked twice
if (useShutdownHook) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
// If JULI is being used, re-enable JULI's shutdown to ensure
// log messages are not lost
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
true);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
此处就会移除关闭钩子!
2.虚拟机非正常退出,比如用户按下ctrl+c、OutofMemory宕机、操作系统关闭等。在退出时执行必要的挽救措施。
public class JVMHook {
public static void start() {
System.out.println("The JVM is started");
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
System.out.println("The JVM Hook is execute");
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public static void main(String[] args) {
start();
System.out.println("The Application is doing something");
byte[] b = new byte[5000 * 1024 * 1024];
System.out.println("The Application continues to do something");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
同时添加启动参数:-Xms10m -Xmx10m -XX:MaxPermSize=10m
如上模拟了内存溢出的场景,执行结果如下:
当然,使用钩子要注意竞争条件或死锁问题,建议关闭操作在单个线程中串行执行。同一个JVM最好只使用一个关闭钩子,而不是每个服务都使用一个不同的关闭钩子,使用多个关闭钩子可能会出现当前这个钩子所要依赖的服务可能已经被另外一个关闭钩子关闭了。
1万+

被折叠的 条评论
为什么被折叠?



