如何优雅地关闭JVM?看看钩子函数如何一步实现

2021年09月15日 阅读数:4
这篇文章主要向大家介绍如何优雅地关闭JVM?看看钩子函数如何一步实现,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

?public?void?start(){
??Runtime.getRuntime().addShutdownHook(new?Thread(()->?
????System.out.println("钩子函数被执行,能够在这里关闭资源")
??));
?}
?public?static?void?main(String[]?args)?throws?Exception{
??new?Test().start();
??System.out.println("主应用程序在执行");
?}
}
//控制台输出
//主应用程序在执行
//钩子函数被执行,能够在这里关闭资源markdown


看控制台打印,能够发现,主应用程序执行完以后就会调用钩子函数,接下来就会正式的关闭JVM。

### 二、异常关闭

仍是直接看代码演示,这里咱们演示异常关闭的第二种OOM的状况,咱们能够先设置堆的大小为20M,而后在代码中建立一个500M的对象,这样就会OOM。参数是`-Xmx20M`;

<pre class="custom">`public?class?Test?{
?public?void?start(){
??Runtime.getRuntime().addShutdownHook(new?Thread(()->?
????System.out.println("钩子函数被执行,能够在这里关闭资源")
??));
?}
?public?static?void?main(String[]?args)?throws?Exception{
??new?Test().start();
??System.out.println("主应用程序在执行");
??Runtime.getRuntime().halt(1);
??byte[]?b?=?new?byte[50010241024];
?}
}
//控制台输出
//主应用程序在执行
//钩子函数被执行,能够在这里关闭资源ide


从控制台能够看出,钩子函数在异常关闭的时候依然会被调用。

### 三、强制关闭

这里咱们使用`Runtime.getRuntime().halt()`来演示强势关闭。这个方法和`System.exit`的区别是,`System.exit`会执行钩子函数,可是`Runtime.getRuntime().halt()`不会。

<pre class="custom">`public?class?Test?{
?public?void?start(){
??Runtime.getRuntime().addShutdownHook(new?Thread(()->?
????System.out.println("钩子函数被执行,能够在这里关闭资源")
??));
?}
?public?static?void?main(String[]?args)?throws?Exception{
??new?Test().start();
??System.out.println("主应用程序在执行");
??Runtime.getRuntime().halt(1);
?}
}
//控制台输出
//主应用程序在执行函数


从上面代码的输出能够看出,调用了`Runtime.getRuntime().halt(1)`就会强制关闭JVM,钩子函数来不及执行就关闭了。而使用`System.exit`依然会执行。因此通常使用`System.exit`来关闭JVM。

### 四、移除钩子函数

上面演示了钩子函数的做用,有时候咱们想移除也比较简单。

<pre class="custom">`public?class?Test?{
?public?static?void?main(String[]?args)?throws?Exception{
??//new?Test().start();
??Thread?willNotRun?=?new?Thread(()?->?
???System.out.println("Won't?run!"));
??Runtime.getRuntime().addShutdownHook(willNotRun);
??System.out.println("主应用程序在执行");
??Runtime.getRuntime().removeShutdownHook(willNotRun);
?}
}
//控制台输出
//主应用程序在执行oop


OK,钩子函数的基本操做就写到这,使用起来比较简单,不过我以前看过Spring的启动流程,因此又去那个启动流程看了一波,发现也使用到了钩子函数。

## 2、典型应用场景

### 一、Spring使用

Spring在关闭上下文的时候,可使用钩子函数来关闭残留的资源。方法是使用`ApplicationContext`注册一个钩子函数便可。

<pre class="custom">`ApplicationContext.registerShutdownHook();br/>//上面的这句代码能够分析进去看看
public?void?registerShutdownHook()?{
????if?(this.shutdownHook?==?null)?{
??????this.shutdownHook?=?new?Thread()?{
????????@Override
????????public?void?run()?{
??????????//Spring正常关闭
??????????doClose();
????????}
??????};
??????//调用钩子函数关闭残留资源
??????Runtime.getRuntime().addShutdownHook(this.shutdownHook);
????}
}this



从源码能够看出,Spring其实也是调用了Java的钩子函数进行关闭的。

### 二、其余使用

我在不少博客中也看到了spark和hadoop的关闭,因为我没看过源码,因此这里我说一下结论,对于其余的使用场景,基本上也是调用了Java的钩子函数来执行的。