
文章插图
接下来使用命令gdb bin/java core.44729打开core文件 , 发现是rocksdb start thread时挂的 , 挂在libstdc++里 , 这是glibc库 , 基本不可能出问题 , 因此该堆栈可能是表象 , 有其他原因导致start thread失败 。

文章插图
注意到打开core文件时 , 有太多线程-LWP轻量级进程 。

文章插图
然后在gdb里用info threads , 发现有三万多个线程 , 都在wait锁状态 , 基本确认三万多个线程 , 导致内存太大 , 创建不出来新的线程 , 因此挂在start thread里 。

文章插图
接着分析三万多个线程都是什么线程 , 随机选几十个线程 , 打出每个线程的堆栈 , 可以看到大部分线程都是jvm线程 。因为rocksdb创建出来的线程是:
从/tmp/librocksdbjni8646115773822033422.so来的;而jvm创建出来的线程都是从/usr/java/jdk1.8.0_191-amd64/jre/lib/amd64/server/libjvm.so来的 , 这部分线程占了大部分 。

文章插图

文章插图
因此问题出在Java代码里 , 产生core.pid文件的进程 , 虽然没有产生crash log , 但也是因为Java 线程太多 , 导致C++代码创建线程时挂掉 。至于为什么Java线程太多请看Java内Crash 。
另外 , core.pid完整的保留了C++组件Crash时的现场 , 包括变量、寄存器的值等 , 如果真的因为C++组件有Bug而Crash , 例如空指针等 。首先自行找到C++源码 , 找出怀疑空指针的变量{variableName} , 通过在gdb里执行命令:p {variableName} , 可以看出每个变量的值 , 从而找出空指针的变量 。
Java内Crash排查Java内Crash的原因如OOM等 , 需要配置JVM的如下参数:
-XX:ErrorFile
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath 。
JVM内发生Crash时 , 会在-XX:ErrorFile配置的路径下生成crash log 。而-XX:+HeapDumpOnOutOfMemoryError、-XX:HeapDumpPath用于发生OOM时生成Dump堆 , 用于还原现场 。下图所示为产生的crash log 。可以看到创建线程时发生OutOfMemory导致进程挂掉 。

文章插图
从下图crash log可以看到有两万四千个Datanode State machine Thread线程都在等锁 。到此确认上文Java调用C++发生Crash 产生core.pid的进程和产生crash log的进程都是因为两万多个Datanode State Machine Thread挂掉 。

文章插图
接着分析为何有两万多个Datanode State Machine Thread , 代码里可以看到该线程用线程池newCacheThreadPool创建 。该newCacheThreadPool在没有线程可用 , 例如线程都在等锁的情况下 , 会创建新的线程 , 因此创建了两万多个线程 。接着分析Datanode State Machine Thread等的什么锁 。在进程的线程数超过5000时 , 用jstack -l pid > jstack.txt打出所有线程的状态 。
可以看到几乎所有Datanode State Machine Thread在等锁 , 而只有一个Datanode State Machine Thread – 5500 拿到了锁 , 但是卡在提交RPC请求submitRequest 。至此Java调用C++发生Crash 和Java内Crash的原因找到 。

文章插图

文章插图
死锁log4j导致的死锁jstack打出的死锁信息如下所示 。grpc-default-executor-14765线程拿到了log4j的锁 , 在等RaftServerImpl的锁;grpc-default-executor-14776线程拿到了RaftServerImpl的锁 , 在等log4j的锁 , 导致这两个线程都拿到了对方等待的锁 , 所以造成两个线程死锁 。可以看出 , 仅仅打日志的log4j , 不释放锁是最值得怀疑的地方 。最后发现log4j存在死锁的缺陷[4] 。该缺陷在log4j2得到解决 , 升级log4j即可 。
推荐阅读
- 排名前5的JavaScript框架
- Linux 上部署 Java 应用绕不开的命令,撒花啦
- java 实现 SelectSort 选择排序算法详解
- JavaScript数据结构——队列的实现
- 常用Java开发工具介绍
- JAVA中常见的阻塞队列详解
- Java线上CPU100% 问题排查
- JavaScript,如何在字符串中找到一个字符?
- 支付宝app支付服务端的实现-Java版
- 电脑卡顿严重,更换硬盘或内存条是最后选择,可使用这种方法解决
