Tomcat_假死_解决案例1

排查背景

2023-07-15上午业务人员反映系统出现卡死现象,持续近20min无法访问网页,进一步排查发现:tomcat一直处于运行状态但是就是无法对请求进行响应,即tomcat发生假死现象

Linux命令查询系统相关信息

# 查看系统cpu,内存和磁盘占用情况
top #cpu
free -h #内存
df -hl #磁盘

# 如果cpu占用很高
# 查询到7020这个进程有异常,在继续查看具体异常线程
top -Hp 7020 
 PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 23328 root      20   0 2538892 164144  11856 S  90.0  8.7   0:00.00 java
printf "%x\n" 23328 # 进制转换nid
5b20
jsack -l 7020 | grep '5b20' # 根据nid查询
http-bio-6379-exec-200 #8869954 daemon prio=5 os_prio=0 tid=0x00007f74a81f6800 nid=0x5b20 waiting for monitor entry [0x00007f742457f000]

# 如果内存占用高
# 查看指定pid的所有JVM信息
jinfo pid

# 查看指定进程下JVM内存占用情况
jmap -heap pid
# 查看活跃对象,紧急情况记录到文件中
jmap ‐histo:live pid | more
# 情况紧急将内存使用情况dump到文件中
jmap -dump:format=b,file=dump_tomcat_4728.dat pid
# ive的意思是只保留存活的对象。否则所有对象(包括已经被gc的对象)都会被输出,这在线上环境heap比较大,运行时间比较长的情况线下,会导致heap过大,没有必要。
jmap -dump:live,format=b,file=/data/tmp/heapdump.hprof pid

# -F是进程无法响时使用 (最好几分钟后再做一次快照 多一点对照)
jstack -F pid > file.txt

# 大文件日志查看最近 具体行数自行测试
tail -n  xxxx filename
tail -n 100 file | head -n 3 # 先获取文件的后面100行

排查原理支持

  1. 熟悉JVM原理的应该知道GC root,如果是内存占用高,首先排查GC root有哪些
    • 虚拟机栈中引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中 JNI 引用的对象
  2. 通过线程的栈信息排查 本地方法栈虚拟机栈中引用的对象(JVM假死情况应该是发生在堆栈中对象创建销毁频繁)
  3. 通过对堆内存的快照文件排查可以分析方法区中的静态变量常量引用的情况
  4. 可以使用idea 的Profiler工具(工具路径:Run|Open Profiler Snapshot)已经jvisualvm和eclipse的MAT内存分析工具(个人推荐Idea和MAT工具,jvisualvm文件太大无法打开)

排查过程简述

  1. 通过上述的Linux命令发现系统cpu和JVM的内存占用接近100%,其中JVM内存信息如下 PSYoungGen和ParOldGen占用99%
Heap
 PSYoungGen      total 1396736K, used 1332687K [0x000000071b300000, 0x0000000786e00000, 0x00000007c0000000)
  eden space 1332736K, 99% used [0x000000071b300000,0x000000076c873f10,0x000000076c880000)
  from space 64000K, 0% used [0x0000000780c00000,0x0000000780c00000,0x0000000784a80000)
  to   space 36352K, 0% used [0x0000000784a80000,0x0000000784a80000,0x0000000786e00000)
 ParOldGen       total 5401600K, used 5401292K [0x00000005d1800000, 0x000000071b300000, 0x000000071b300000)
  object space 5401600K, 99% used [0x00000005d1800000,0x000000071b2b3098,0x000000071b300000)
 Metaspace       used 104056K, capacity 115418K, committed 117336K, reserved 1155072K
  class space    used 10307K, capacity 11787K, committed 12160K, reserved 1048576K
  1. 执行shell jmap -dump:live,format=b,file=/data/tmp/heapdump.hprof pid 导出dump文件
  2. 分析dump文件发现其中有个对象名:XXXX的活跃的实例数非常多,通过查看改对象被引用信息(incoming References or shortest Paths)得知是位于本地缓存,即本地缓存中存在内存泄露情况
  3. 分析线程的栈信息发现tomcat的200个线程全部耗尽且都在等待执行(由于系统的连续FullGC),从而导致系统无法对新的请求进行响应

排查问题总结

  1. 为什么tomcat一直处于运行状态,发生内存泄露不应该直接报OOM错误,tomcat内存溢出后退出运行吗?
    这种假死的状态一般来说是发生内存泄露的过程非常缓慢,当泄露的内存刚好达到一定比例,这个比例取决于【新增的对象填满老年代的时间】/【每次FullGc的持续时间】越小说明FullGC越频繁,系统卡顿越明显,当达到一个临界值后就会产生彻底卡死的现象,一般来说假死是一个逐渐变慢的一个过程,当系统越慢又会导致新增到老年代的对象增多,是一个恶性循环。

最后的最后

如对此类问题有疑问,可以一起讨论讨论。

你可能感兴趣的:(JVM虚拟机,tomcat,jvm)