作者:冯冬冬 2021-03-28 09:17:18
云计算
虚拟化 程序的启动很简单,启动的时候通常会做一些预加载资源的操作。但是有时候关闭的时候,启动的时候预加载的资源并没有完全清理干净,因此可以使用钩子函数来完成。
创新互联2013年至今,是专业互联网技术服务公司,拥有项目成都网站建设、成都网站制作网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元邳州做网站,已为上家服务,为邳州各地企业和个人服务,联系电话:18982081108
程序的启动很简单,启动的时候通常会做一些预加载资源的操作。但是有时候关闭的时候,启动的时候预加载的资源并没有完全清理干净,因此可以使用钩子函数来完成。
直接看一张图吧,本图来自博客园的BarryWang,特在此说明。
从上面可以看到,JVM关闭主要分为了三类,第一种是正常的关闭,第二种是异常关闭的情况,第三种是强制关闭的情况。对于前两种方式我们可以使用钩子函数优雅的关闭,但是强制关闭的时候钩子函数并不起作用。
有了这些概念,我们直接使用一个案例进行演示,再进行分析。
直接看代码吧,
- 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("主应用程序在执行");
- }
- }
- //控制台输出
- //主应用程序在执行
- //钩子函数被执行,可以在这里关闭资源
看控制台打印,可以发现,主应用程序执行完之后就会调用钩子函数,接下来就会正式的关闭JVM。
还是直接看代码演示,这里我们演示异常关闭的第二种OOM的情况,我们可以先设置堆的大小为20M,然后在代码中创建一个500M的对象,这样就会OOM。参数是-Xmx20M;
- 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[500*1024*1024];
- }
- }
- //控制台输出
- //主应用程序在执行
- //钩子函数被执行,可以在这里关闭资源
从控制台可以看出,钩子函数在异常关闭的时候依然会被调用。
这里我们使用Runtime.getRuntime().halt()来演示强势关闭。这个方法和System.exit的区别是,System.exit会执行钩子函数,但是Runtime.getRuntime().halt()不会。
- 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。
上面演示了钩子函数的作用,有时候我们想移除也比较简单。
- 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);
- }
- }
- //控制台输出
- //主应用程序在执行
OK,钩子函数的基本操作就写到这,使用起来比较简单,不过我之前看过Spring的启动流程,所以又去那个启动流程看了一波,发现也使用到了钩子函数。
Spring在关闭上下文的时候,可以使用钩子函数来关闭残留的资源。方法是使用ApplicationContext注册一个钩子函数即可。
- ApplicationContext.registerShutdownHook();
- //上面的这句代码可以分析进去看看
- public void registerShutdownHook() {
- if (this.shutdownHook == null) {
- this.shutdownHook = new Thread() {
- @Override
- public void run() {
- //Spring正常关闭
- doClose();
- }
- };
- //调用钩子函数关闭残留资源
- Runtime.getRuntime().addShutdownHook(this.shutdownHook);
- }
- }
从源码可以看出,Spring其实也是调用了Java的钩子函数进行关闭的。
我在很多博客中也看到了spark和hadoop的关闭,由于我没看过源码,所以这里我说一下结论,对于其他的使用场景,基本上也是调用了Java的钩子函数来执行的。
在关闭JVM的时候,我们可以封装钩子函数去优雅的关闭线程。不过在使用的时候还需要注意以下几个方面:
多个钩子会并发执行,JVM并不保证它们的执行顺序;因此最好是在一个钩子中执行一系列操作。
在关闭钩子中,不能执行注册、移除钩子的操作,否则JVM抛出 IllegalStateException。也不能使用System.exit(),前面提到System.exit()会触发钩子函数的执行,但是Runtime.halt()可以,因为Runtime.halt()可以强制关闭。
钩子函数主要用于关闭残留资源,因此不要有一些耗时的操作。
OK,先写到这。
本文转载自微信公众号「愚公要移山」,可以通过以下二维码关注。转载本文请联系愚公要移山公众号。
分享题目:如何使用钩子优雅的关闭JVM?
文章转载:http://www.mswzjz.cn/qtweb/news43/43193.html
攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能