关于java:故障分析-让top命令直接显示Java线程名-解析OpenJDK的一个bug修复

40次阅读

共计 2305 个字符,预计需要花费 6 分钟才能阅读完成。

作者:阎虎青

DBLE 开源我的项目负责人,负责分布式数据库中间件研发工作;继续专一于数据库方面的技术,始终在一线从事开发;对数据复制、读写拆散、分库分表有深刻的了解和实际。

本文起源:原创投稿

* 爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。


话说有一天,dble 团队的测试小伙伴要做性能方面的调优,须要理解调优 dble 的原理与办法,于是我就丢给他一份文档让他好好学习一下:

https://actiontech.github.io/…。

测试同学充斥鄙夷的看完了文档,而后问我,图中这个命令的目标是做什么的?

用 Linux 原生的 top 命令不就行了么?你费那事干啥?

体现的机会来了,于是我很急躁的通知他,原生 top 命令尽管对 cpu 统计的更加精确,然而对 Java 线程并不敌对,线程名都显示 java,和利用理论的线程名对不起来,须要通过 jstack 来核查一下,就像这样的,一边说着,我一边开始了演示。

首先,top 命令提供了一个正在运行的动静实时视图。它能够显示零碎摘要信息以及以后由 Linux 内核 治理的过程或线程列表。显示的这些信息的类型、程序是可配置并且长久化。。。(“说重点,别凑字数”,测试同学打断了滔滔不觉的我)。

好的,说重点,默认 top 显示的是单个过程中所有线程的指标的总和,咱们能够通过参数 -H 来指定显示线程的信息,另外参数 -p 还能够指定具体的过程 id。

执行看看:

top -H -p `pidof java`

后果大略是这样的:

这个后果有什么用呢?还得通过 jstack 命令来打印一份堆栈(当然,如果线上环境可能要承当肯定的危险)

jstack -l `pidof java` > /tmp/dble_jstack.log

有了这两个后果,咱们就可以看某个线程号在利用中具体对应哪个线程。

具体怎么做呢? 比方咱们拿着 10849 这样一个线程号,把它转为 16 进制的数字

printf "%x\n" 10849
2a61

而后,再在 jstack 的后果中查找线程的名字。

cat  /tmp/dble_jstack.log | grep "nid=0x2a61"
"BusinessExecutor0" #23 daemon prio=5 os_prio=0 tid=0x00007f95dc620800 nid=0x2a61 waiting on condition [0x00007f96281f6000]

当然,如果只须要线程名字,这就足够了,两行命令也能够拼接在一起。如果还须要更多的上下文信息,能够查看 grep 手册找响应的参数比方 - A 还有 -B,甚至能够关上文件查找。

我还通知他,理论运维工作中,如果发现 java 过程的 CPU 飙高了,怎么排查呢?也是同样的办法,先通过 top 命令找到 CPU 高的线程,而后通过 jstack 晓得这个线程在做什么,尝试解决 CPU 高的问题, 并且据我教训,绝大部分是因为 gc 问题,另外还遇到过 nio 的 epoll bug。

测试同学静静地听我说完后半程的介绍以及演示,在他本人的 Ubuntu 终端上敲了同样的一行命令:

top -H -p `pidof java`

而后指着后果问我:这不是有线程名么?

这。。。大写的难堪加打脸现场,满头问号我只好乖乖认怂,并去钻研下到底是为什么 top 命令在他的机器上比我的机器上听话。

通过几个昼夜不眠不休的考察(并没有),我终于查出了是否显示线程名的起因。原来早在边远的 2011 年就有人提出了疑难(参见 JDK-7102541),并且在 JDK-8179011 给出了更加简略的形容。修复也早在 2019 年就有人做了,并且 openjdk 把它 backport 到 8u222 版本上,也就是说从那之后的 openjdk 版本就曾经修复了这个问题,然而到我这篇文章书写的时候,oracle jdk 的 8u301 依然没有修复这个问题。所以读者们晓得该怎么抉择 jdk 了吧。

既然 bug 曾经修了,就忍不住就想看看具体是怎么实现的。这里咱们次要关怀一下 Linux 平台的实现。这里还有个有意思的事件,咱们其实能够看见这是一位来自 SAP 的大牛在 2014 年就曾经实现的性能。

咱们来具体看代码,首先在线程实现的层面将线程的名字 set 进去:

而后是对 set_native_thread_name 办法的具体实现,在 Linux 平台下代码如下:

能够看到,这里截取了线程名的前 15 个字符,而后调用了 Linux::_pthread_setname_np 办法。在看下这个办法做了什么:

能够看出,这里其实是通过 dlsym 调用操作系统的 pthread_setname_np 办法。

至此,考察完结。咱们总结以下几点内容:

  1. 如果应用 Oracle jdk8 或者更早的 jdk,那么还得通过 jstack 或者其余办法来对应线程号和逻辑线程名称。
  2. 如果应用 openjdk8,倡议降级到 222 之后,这样能够通过 top 命令间接看到线程的名称,放慢诊断。
  3. 倡议利用在设置线程名时,尽量在 15 个字符内表白出惟一的含意,便于察看和剖析,当然,这一点 dble 做得不好,会在之后进行调整和批改
  4. 当然,社区还有一些其余工具,比方阿里的 Arthas 应该也能实现线程 id 和名字对应的性能,不过引入第三方总是件麻烦的事件,还是原生的更香。

参考文档:

http://hg.openjdk.java.net/jd…
https://bugs.openjdk.java.net…
https://bugs.openjdk.java.net…
https://bugs.openjdk.java.net…
https://man7.org/linux/man-pa…
https://man7.org/linux/man-pa…

正文完
 0