使用JDK飞行记录器和一些SQL查找Java线程泄漏 - Gunnar Morling


配备 JDK Flight Recorder、JDK Mission Control 和 JFR Analytics,识别和修复 Java 应用程序中的线程泄漏正在成为一项相对简单的任务。

分析可疑线程泄漏的通常起点是进行线程转储,例如使用jstack CLI 工具或通过JDK Mission Control

但是线程转储本身只是给定时间线程状态的快照,即它不会告诉您线程数如何随时间变化(也许有许多线程启动但又再次停止?),而且它也不向您提供有关原因的信息,即您的应用程序的哪一部分正在启动所有这些线程。它是发生在您自己的代码库中,还是发生在某些第 3 方依赖项中?虽然转储中的线程名称和堆栈可以让您了解一些信息,但这些信息不足以进行最终的根本原因分析。

幸运的是,Java 的内置事件记录器和性能分析工具JDK Flight Recorder公开了识别线程泄漏及其原因所需的所有数据。那么让我们来看看细节,永远告别那些讨厌的线程泄漏!

要查看的第一个 JFR 事件类型是jdk.JavaThreadStatistics:默认情况下每秒记录一次,它跟踪活动、累积和峰值线程数。

如果活动线程的数量不断增加,再也不会下降——很明显这是线程泄漏。

这时,另外两种 JFR 事件类型可以派上用场:jdk.ThreadStartjdk.ThreadEnd

  • 前者捕获线程启动时的所有相关信息:时间戳、新线程和父线程的名称,以及启动子线程时父线程的堆栈跟踪。
  • 后一种事件类型将在线程结束时被记录。

如果我们发现许多线程启动事件起源于相同的代码位置而没有相应的结束事件(通过事件中包含的线程 ID 关联),这很可能是线程泄漏的来源。

这种事件分析是JFR分析的一个完美用例。这个工具允许你使用标准的SQL来分析JFR的记录(利用引擎盖下的Apache Calcite)。在JFR分析中,每个事件类型都由它自己的 "表 "表示。找到没有匹配结束事件的线程开始事件,就像在两个事件类型上运行LEFT JOIN一样简单,并只保留那些没有join伙伴的开始事件。

信息点击标题