System.exit()方法不能退出JVM的问题排查

环境说明:

spring-boot 2.3.1
jdk8
apache-dubbo 2.7.1

原因:

dubbo中,提前注册shutdownHook导致死锁问题

业务场景:

项目需要在启动时,缓存一些业务数据,所以在利用相关bean实现InitializingBean接口,实现afterPropertiesSet()方法,如果在afterPropertiesSet()方法缓存数据出现异常,则使用System.exit()方法退出JVM,并且该bean是需要提前实例化的。

具体代码分析:

org.springframework.context.support.AbstractApplicationContext中

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			prepareRefresh();
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// 实例化非懒加载的对象。这步会牵涉到dubbo去生成dubbo代理对象,导致提前注册shutdownHook
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}
			catch (BeansException ex) {
				destroyBeans();
				cancelRefresh(ex);
				throw ex;
			}
			finally {
				resetCommonCaches();
			}
		}
	}

dubbo导致的提前注入shutdownHook

本来应该是刷新springContext完成之后进行的,如下

private void refreshContext(ConfigurableApplicationContext context) {
		refresh((ApplicationContext) context);
        //refresh完成之后,注册hook,
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

org.springframework.boot.SpringApplication

上述注册hook前置之后,并且在需要提前实例化的bean创建过程中,触发了System.exit(),导致JVM去调用shutdownHook,而之前由Main线程持有的startupShutdownMonitor对象是没有释放的(需要refreshContext执行完之后才会释放),现在main线程由于System.exit()去调用runHooks()方法,如下图:
在这里插入图片描述

而SpringContextShutdownHook线程又需要去获取startupShutdownMonitor锁,导致无法获取执行完成,所以,hook.join()方法就一值将main阻塞下去,导致JVM无法退出。

此刻,main线程和SpringContextShutdownHook线程的状态分别如下:
main线程:
在这里插入图片描述

SpringContextShutdownHook线程:
在这里插入图片描述

解决办法
  1. 将apache-dubbo进行升级,尝试了下2.7.14,是可以的
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值