Java虚拟机ShutdownHooks

来源:互联网 发布:英语离线翻译软件 编辑:程序博客网 时间:2024/06/17 04:17

Java虚拟机为开发人员提供了一个回调入口,开发人员通过这个回调入口,向Java虚拟机注册任意一段代码,让Java虚拟机在将要结束运行之前,执行这段代码。这个机制通常被用来做一些资源的清理工作,tomcat容器源码中就有用到这个机制,现在对这个机制做一个介绍。


demo

public static void main(String[] args) {    Runtime.getRuntime().addShutdownHook(new Thread(){        @Override        public void run() {            System.out.println("The hook is executing");        }    });    System.out.println("The application is going to be stopped");}

但是使用Shutdown Hook时,会有一些注意的地方:

  • 在某些情况下向JVM注册的hooks不会被执行
    JVM并不保证这些hooks一定会被执行,hooks是否会被执行受系统内部及外部因素影响。

    1. JVM遇到严重的内部错误的时候
    2. 用户通过操作系统向Java虚拟机进程发送SIGKILL,要求操作系统立即停止虚拟机进程时
    3. 调用Runtime.halt()停止虚拟机执行

    只有当JVM正常退出时,hooks才会被执行,应用程序抛出的运行时异常,以及JVM抛出的错误(Error)不会影响hook的执行。

  • Shutdown Hook处于执行状态时,仍有可能被强制停止
    一旦hooks启动之后,仍然可能由于各种原因被停止,需要注意的是,不能在hooks中进行一些非常耗时的操作,以及资源申请操作,因为这样会造成严重问题,如死锁(JVM不会保证hooks的执行顺序,如果hooks中的代码缺少足够的同步,会造成死锁,及不一致)

  • JVM不保证Shutdown Hooks的执行顺序
  • Shutdown Hooks一旦开始执行,只能通过Runtime.halt()命令来停止hooks的执行,调用System.exit()并不能停止hooks的执行
  • 如果Shutdown Hook中抛出了异常,并且没有被捕获,那么JVM会使用default exception handler来处理异常

tomcat中Shutdown hook的应用

tomcat在启动容器之后,会向JVM注册一个Shutdown hook用于确保tomcat应用即使是在异常退出之后,仍然能够保证资源被正确的释放掉,下面是org.apache.catalina.startup.Catalina类的start()方法的片段

// Register shutdown hookif (useShutdownHook) {    if (shutdownHook == null) {        shutdownHook = new CatalinaShutdownHook();    }    Runtime.getRuntime().addShutdownHook(shutdownHook);    // If JULI is being used, disable JULI's shutdown hook since    // shutdown hooks run in parallel and log messages may be lost    // if JULI's hook completes before the CatalinaShutdownHook()    LogManager logManager = LogManager.getLogManager();    if (logManager instanceof ClassLoaderLogManager) {        ((ClassLoaderLogManager) logManager).setUseShutdownHook(                false);    }}

下面是CatalinaShutdownHook的定义:

protected class CatalinaShutdownHook extends Thread {    @Override    public void run() {        try {            if (getServer() != null) {                Catalina.this.stop();            }        } catch (Throwable ex) {            ExceptionUtils.handleThrowable(ex);            log.error(sm.getString("catalina.shutdownHookFail"), ex);        } finally {            // If JULI is used, shut JULI down *after* the server shuts down            // so log messages aren't lost            LogManager logManager = LogManager.getLogManager();            if (logManager instanceof ClassLoaderLogManager) {                ((ClassLoaderLogManager) logManager).shutdown();            }        }    }}

可以看到tomcat使用Shutdown Hook来确保,Server和LoggerManager正常关闭,为了避免因Server关闭先于LoggerManager导致日志消息丢失的问题,LoggerManager并没有直接使用Shutdown Hook来关闭。