IBM J9 JDK thread dump analysis

工作中偶尔遇到一些基于 IBM J9 的 JDK 应用, 由于大部分时间都是和 Linux + Hotspot JDK 打交道, 对于 IBM J9 JDK 不是很熟悉, 不过 IBM 有很多工具可以分析这些东西, 记录一下, 以备以往.

首先, 它的 thread dump 在一个叫 Java core dump 的文件里, 这里并不是 *nix 的一般意义上的 core dump, 只是 java 的 thread 相关的一些 dump 内容. 如何 获得一个 thread dump, 参看这里: https://www.ibm.com/support/knowledgecenter/SS3JSW_5.2.0/com.ibm.help.performance_mgmt.doc/SIPM_ThreadDump.html

# 获得文件名以 javacore*** 开头
kill -QUIT <java_pid>
find /app -name javacore*

分析工具使用:
IBM Thread and Monitor Dump Analyzer for Java, 下载地址: ftp://public.dhe.ibm.com/software/websphere/appserv/support/tools/jca/jca457.jar

官方地址: https://www.ibm.com/support/pages/java-core-debugging-using-ibm-thread-and-monitor-dump-analyzer-java

Java 如何查看 Direct Memory 大小

遇到过几次 Java NIO 使用 DirectMemory 导致的 OOM, 在诊断的过程中, 其中一个问题就是如何定位当前使用的 Direct Memory 的大小.

在早期, 都是通过 Java NMT Native Memory Tracking 的工具, 首先启动的过程中添加 tracking 的参数, 然后启动后去重现, 然后使用 jcmd 命令去查看各种内存区域的使用情况.

当然, 也可做一个 heap dump, 通过 heap dump 中的 DirectByteBuffer 等实例的 capacity 来确定到底使用了多大的大小.

上次在 Java Performance 这本书的第二版看到, JVM 带的 MBean 就包含了一个 MBean, 它就能确定当前 JVM 使用 Direct Memory 的大小. 所以, 如果你在桌面环境, 可以通过 JConsole 或 JVisualVM 等工具去查看这个 MBean, 如那本书里的截图:
MBean.png

或者 google 一下 jmx command line, 能看到很多人写的命令工具, 去连接 JVM, 然后查看 MBean 状态的, 也能达到同样的目的.

我们公司的框架工具自动提供在页面查看各种 MBean 状态的功能, 非常好用. 直接能看到 DirectMemory 已经达到设置是上限.

Linux core dump

什么是 core dump

core dump 是一个进程的内存瞬时映像. core dump 这个名字来自于最早的计算机的内存技术. 使用 gdb 等工具可以读取 core dump, 然后分析进程当时的运行状态和内存信息.
https://man7.org/linux/man-pages/man5/core.5.html

如何获得 core dump

  1. 给进程发各种能产生 core dump 的 signal, 比如 SIGQUIT, SIGKILL 等.
  2. 使用 gdb 等 debug 工具对正在运行的进程发送 signal, 产生 core dump.

由于 1) core dump 会占用大量的磁盘空间, 2) core dump 可能包含内存的里面某些敏感数据, 所以 Linux 尽管在得到要 trigger core dump 的信号情况下, 也由于 ulimit 的控制, 默认不产生 core dump. 因此, 如果要捕获 core dump, 首先要选择打开 core dump limit 设置.
使用 ulimit -a 命令查看当前 ulimit 设置值:

eric@host:~$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 31660
max locked memory       (kbytes, -l) 16384
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 31660
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
修改 ulimit 设置

可以使用 ulimit -c unlimited 命令, 但是可能非 root 用户没有权限.
在 /etc/security/ 目录下, 有个 limits.conf 和 limits.d 目录(如果有 limits.d 则 limits.conf 不生效, 可以认为 limits.conf 是模板). 在 limits.d 目录下新建一个以 .conf 结尾的文本文件, 添加要改的一行.

eric@host:~$ ls /etc/security/ | grep limit
limits.conf
limits.d

core dump 的文件名

正常情况下 core dump 的文件名没有后缀, 文件名就是 core. 这个名字可以通过 /proc/sys/kernel/core_pattern 来改. 这个名字还可以使用一些模板字符来替换. 比如 %h: 主机名; %p: 进程号 %t: Epoch 毫秒数, %u: user id;

如何分析 core dump

关于 JVM TLAB

  1. 什么是 TLAB?
    每个线程在 Eden 区申请的属于该线程的一块空间, 该线程在自己的 TLAB 申请空间, 不需要同步. 但是申请 TLAB 需要锁同步, 在 TLAB 之外申请空间, 需要锁同步.
  2. 关于 TLAB 的参数
    使用如下命令可以看到所有跟 TLAB 相关的参数:
$java -XX:+PrintFlagsFinal -version | grep TLAB

比如我在 JDK 11 上, 可以看到如下有关 TLAB 的参数:

   size_t MinTLABSize                              = 2048                                      {product} {default}
     bool ResizeTLAB                               = true                                   {pd product} {default}
    uintx TLABAllocationWeight                     = 35                                        {product} {default}
    uintx TLABRefillWasteFraction                  = 64                                        {product} {default}
   size_t TLABSize                                 = 0                                         {product} {default}
     bool TLABStats                                = true                                      {product} {default}
    uintx TLABWasteIncrement                       = 4                                         {product} {default}
    uintx TLABWasteTargetPercent                   = 1                                         {product} {default}
     bool UseTLAB                                  = true                                   {pd product} {default}
     bool ZeroTLAB                                 = false                                     {product} {default}
  1. 什么是 PLAB?