Analysis and Solution of JVM Deadlock Problem

This article analyzes a JVM deadlock issue encountered in a Tomcat-based application, detailing the problem's cause, analysis using jstack, and the solution involving ClassLoader isolation and ParallelCapable registration.

Beike Product & Technology
Beike Product & Technology
Beike Product & Technology
Analysis and Solution of JVM Deadlock Problem

随着业务的发展,相关系统的规模越来越大,系统之间的关系随业务的变化、组织的调整、人员的流动、新技术的引入等原因逐渐错综复杂,各式各样的挑战也逐渐凸显:

历史负担很重,能否低成本的落地微服务架构?

业务极端复杂,服务的边界划分是否合理?

系统依赖繁琐,如何理清整体拓扑、如何进行监控报警、如何平滑上线?

为了解决这一系列的相关问题,我们业余自研了一套服务化基础设施 dkimi ,自动梳理系统拓扑、自动提供各种监控统计、智能量化系统结构的合理度与复杂度、全链路流量收集与回放测试 …… 我们希望以一种完全 业务无侵入 的方式赋予我们的业务系统统一的系统性功能、赋予我们的业务系统 服务自治 的能力,让业务系统更加智能。

DKIMI - Do Keep It More Intelligent

从技术上来讲, dkimi 本质上是一个 Java Agent,基于 java.lang.instrument,通过字节码修改提供各种增强功能。 dkimi 跟业务逻辑运行在同一 JVM 进程中,在任何情况下都需要保证对业务不产生干扰,所以我们设计了独立的 ClassLoader 来加载自身的类,期望与业务充分隔离,尽可能避免出现字节码冲突。

期望很美好,现实很残酷。这次遇到的问题就跟 dkimi 里的 ClassLoader 有关,跟 Tomcat 的 WebappClassLoader 实现有关,也跟 JVM 的类加载机制有关。

过去一年里, dkimi 在测试环境下大放异彩,也逐渐开始推广到生产环境。在逐步上线了几十个业务系统、几百个实例并长时间观察稳定之后我们将 dkimi 推广到了最核心的业务系统之一,下称 AF。 AF 是一个基于 Tomcat 容器部署的 web 应用,在生产环境部署了 6 个实例。最核心的业务系统,逻辑自然十分复杂,启动速度也比较慢。相信大家都知道 Tomcat 容器启动时会先监听端口,接收请求,但是在应用启动完成之前请求会被 hold 住,无法实际处理。当启动时间较长时,监控系统会检测到 499 错误。

某一天上线时发现 AF 的其中 1 个实例在 dkimi 输出已经实际启动完成后仍然无响应, access log 无输出,呈僵死状。这是怎么了?为什么其他系统没问题?为什么同样的系统其他实例没问题?是上线的新功能有漏洞吗?

胡乱猜测与手足无措都无济于事,面对这样的问题,还是要祭起 jstack 这柄利器。

相信大家都知道,死锁的一般原因是:存在嵌套加锁,且有至少两个逻辑加锁的顺序不一致。

我们来看看这两个线程的调用栈是不是存在这样的情况。 "catalina-exec-235" 的调用栈如下,确实先锁了 WebappClassLoader,在等待 DkimiPluginClassLoader 的锁:

"catalina-exec-96" 的调用栈如下,只看到在等待 WebappClassLoader 的锁,并没有看到对 DkimiPluginClassLoader 加锁,为什么死锁了呢?

如上一篇文章所说,处理线上问题,最要紧的一定是先尽快恢复服务,减少业务损失。 运维同学当机立断,在收集完相关现场之后,即刻先下掉了 dkimi 。咱们接下来分析原因。我们来看看 dkimi 里自定义的 ClassLoader 的实现,简化如下:

锁住自己 this,也就是 DkimiPluginClassLoader,尝试加载自身的类。

找不到或者是其他类时,锁住 parent,当下也就是 WebappClassLoader,委派给 parent 去加载。

并没有看到这两段逻辑存在嵌套加锁的情况,为什么线程 "catalina-exec-96" 在尝试抢占 parent(WebappClassLoader)时还锁着 this(DkimiPluginClassLoader)呢??

这绝不单单是 Java 语言层面的原因,是不是下图红框中的调用逻辑里潜藏着不为人知的秘密?

先看看 DubboConsumerSerializer.java 的第 43 行是什么:

很普通的一行代码,目测是触发了加载 java.lang.reflect.Method 类。再看看 jstack -m <pid> 的输出,看看这之间发生了什么:

可以看到,在真正调用到 ClassLoader.loadClass 之前,还有很深的本地方法栈(也就是 JVM 自身的逻辑)。但是这个调用栈是什么意思,为什么看起来跟乱码一样?是不是在这些调用里锁住了 DkimiPluginClassLoader?

简单地解析一下这些看起来像乱码一样的调用是什么意思,以其中一行为例:

了解编译器的同学应该知道,编译器中有一种技术叫做名字修饰,用于解决由于程序实体的名字必须唯一而导致的问题的一种技术。上面的这一行实际上就是被 GCC 修饰后的方法调用,简单解析如下:

到此,这些调用栈我们也能读懂了,这一行的完整含义是:

通过参考 openjdk 相关的源码,确实证明了我们的推断,具体的过程无非是沿着调用逻辑读了一遍。我们直接来看看相关的代码,加锁的逻辑出现在上图中背景飘绿的方法调用里,逻辑很清晰,注释很完整,这里就不展开解释了。

知道了原因之后,解决方案的原理也很简单,无非是要么保证加锁顺序一致,要么不要出现嵌套加锁。

具体可以实施的方法也很多,目前我们先采取了将 DkimiPluginClassLoader 注册为 ParallelCapable 的方式去掉了锁的嵌套。(特别注意:这种方案只在 java7 之后有效)。

从发现无响应到收集完现场并重启花费了 1-2 分钟的时间,造成了一些 499 错误,同时当天业务系统也因为上线启动慢等原因引起了一些 499 错误,因此还与相关业务团队发生了些许误会,还好大家也都很nice,都是希望系统能更加稳定高效。感谢相关小伙伴的理解与支持。

期望越美好,付出的努力就必须越大,无论是技术上的,还是沟通上的。

大胆猜测、小心求证,问题总能找到原因,真相只有一个。

dkimi 提供主动暴露服务是否可用的状态信息,期待运维同学基于此功能的平滑上线、实时负载均衡早日上线,彻底解决实例启动过程中服务不可用导致的相关问题。

后记与启示

从发现无响应到收集完现场并重启花费了 1-2 分钟的时间,造成了一些 499 错误,同时当天业务系统也因为上线启动慢等原因引起了一些 499 错误,因此还与相关业务团队发生了些许误会,还好大家也都很nice,都是希望系统能更加稳定高效。感谢相关小伙伴的理解与支持。

期望越美好,付出的努力就必须越大,无论是技术上的,还是沟通上的。

大胆猜测、小心求证,问题总能找到原因,真相只有一个。

dkimi 提供主动暴露服务是否可用的状态信息,期待运维同学基于此功能的平滑上线、实时负载均衡早日上线,彻底解决实例启动过程中服务不可用导致的相关问题。

后记与启示

从发现无响应到收集完现场并重启花费了 1-2 分钟的时间,造成了一些 499 错误,同时当天业务系统也因为上线启动慢等原因引起了一些 499 错误,因此还与相关业务团队发生了些许误会,还好大家也都很nice,都是希望系统能更加稳定高效。感谢相关小伙伴的理解与支持。

期望越美好,付出的努力就必须越大,无论是技术上的,还是沟通上的。

大胆猜测、小心求证,问题总能找到原因,真相只有一个。

dkimi 提供主动暴露服务是否可用的状态信息,期待运维同学基于此功能的平滑上线、实时负载均衡早日上线,彻底解决实例启动过程中服务不可用导致的相关问题。

广告

dkimi 目前正在面向开源做剥离改造,一个经过生产环境考验的工具,相信大家会喜欢的。下图是 dkimi 自动绘制的系统间拓扑(自主设计实现的自适应布局算法)、以及衡量全系统架构的一些指标。更多更强大的功能敬请期待!

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaJVMdeadlockclassloaderTomcatproduction issue
Beike Product & Technology
Written by

Beike Product & Technology

As Beike's official product and technology account, we are committed to building a platform for sharing Beike's product and technology insights, targeting internet/O2O developers and product professionals. We share high-quality original articles, tech salon events, and recruitment information weekly. Welcome to follow us.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.