java内存泄漏排查

java内存泄漏排查

查看cpu的实时运行情况

输入以下命令

top

输出内容如下

top - 12:42:18 up 17:58,  1 user,  load average: 0.02, 0.05, 0.05
Tasks: 210 total,   1 running, 209 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.1 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  8008100 total,  3337016 free,  3381284 used,  1289800 buff/cache
KiB Swap:  8257532 total,  8257532 free,        0 used.  4190364 avail Mem
 PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                           
 5310 root      20   0 3834388 955156  14744 S   1.0 11.9   8:42.44 java                                                                                              
  868 root      20   0  115408    984    704 S   0.3  0.0   0:00.48 ksmtuned                                                                                          
 3134 devbase   20   0 6487524 838328  16216 S   0.3 10.5   7:14.17 java                                                                                              
 4445 root      20   0 4059928 160240  11852 S   0.3  2.0   1:23.55 java                                                                                              
    1 root      20   0  194088   7292   4204 S   0.0  0.1   0:04.00 systemd   

可以把输出内容分成两块来看

一块是概要信息

top - 12:42:18 up 17:58,  1 user,  load average: 0.02, 0.05, 0.05
Tasks: 210 total,   1 running, 209 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.1 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  8008100 total,  3337016 free,  3381284 used,  1289800 buff/cache
KiB Swap:  8257532 total,  8257532 free,        0 used.  4190364 avail Mem

一块是每个进程详细信息

 PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                           
 5310 root      20   0 3834388 955156  14744 S   1.0 11.9   8:42.44 java                                                                                              
  868 root      20   0  115408    984    704 S   0.3  0.0   0:00.48 ksmtuned                                                                                          
 3134 devbase   20   0 6487524 838328  16216 S   0.3 10.5   7:14.17 java                                                                                              
 4445 root      20   0 4059928 160240  11852 S   0.3  2.0   1:23.55 java                                                                                              
    1 root      20   0  194088   7292   4204 S   0.0  0.1   0:04.00 systemd   

接下来就分别进行介绍

概要信息

概要信息第1行参数
参数含义备注
top - 12:42:18系统当前时间可以不用看手机时间啦
up 17:58系统运行时间看从开机到现在这台电脑跑了多久,显得这台电脑劳苦功高
1 user当前登录用户数:1个看看有没有其他人登着
load average: 0.02, 0.05, 0.05系统平均负载(3个值分别为1分钟/5分钟/15分钟)的平均负载看看刚刚一段时间,电脑运行的累不累,负载越高越辛苦
概要信息第2行参数
参数含义
Tasks: 210 total总进程数
1 running1个进程在运行
209 sleeping209进程个在睡眠(好家伙)
0 stopped0个进程处于停止
0 zombie没有僵尸进程
概要信息第3行参数
参数含义缩写
0.0 us用户空间占用CPU百分比user mode
0.1 sy内核空间占用CPU百分比system mode
0.0 ni用户进程空间内改变过优先级的进程占用CPU百分比low priority user mode (nice)
99.8 id空闲CPU百分比 99.8% (真就偷懒呗)idle task
0.0 wa等待IO的CPU时间百分比I/O waiting
0.0 hi硬件中断时间百分比servicing IRQs
0.0 si软件中断 时间百分比servicing soft IRQs
0.0 st实时时间百分比steal (time given to other DomU instances)
概要信息第4行参数
参数含义
KiB Mem : 8008100 total物理内存总量(KB)
3337016 free空闲内存总量(KB)
3381284 used使用的物理内存总量(KB)
1289800 buff/cache用作内核缓存的内存量(KB)
概要信息第5行参数
参数含义
KiB Swap: 8257532 total交换区总量(KB)
8257532 free空闲交换区总量(KB)
0 used使用的交换区总量(KB)
4190364 avail Mem可用的内存(KB)

接下来介绍详细信息

 PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                           
 5310 root      20   0 3834388 955156  14744 S   1.0 11.9   8:42.44 java                                                                                              
  868 root      20   0  115408    984    704 S   0.3  0.0   0:00.48 ksmtuned                                                                                          
 3134 devbase   20   0 6487524 838328  16216 S   0.3 10.5   7:14.17 java                                                                                              
 4445 root      20   0 4059928 160240  11852 S   0.3  2.0   1:23.55 java                                                                                              
    1 root      20   0  194088   7292   4204 S   0.0  0.1   0:04.00 systemd   
简写全称含义
PIDProcess Id进程ID
USERUser Name当前进程运行的用户
PRPriority优先级
NINice Value负值表示高优先级,正值表示低优先级
VIRTVirtual Image进程使用的虚拟内存总量(KB)。VIRT=SWAP+RES
RESResident size进程使用的、未被换出的物理内存大小(KB)。RES=CODE+DATA
SHRShared Mem size共享内存大小,单位(KB)
SStatus进程状态(D|R|S|T|Z),可以看后面那进程张状态表
%CPUcpuCPU占用百分比
%MEMmemory内存占用百分比
TIME+TIME系统运行时间
COMMANDCOMMAND运行的程序

该表最值得看的就是%CPU和%MEM ,可以看是否内存占用过高或者cpu占用过高。

若cpu占用过高,可能是空轮循,或者发生了频繁的垃圾回收,或者程序访问压力过大

若内存占用过高,可能存在内存泄漏

进程状态表
状态值含义全称
R运行running
D不可中断的睡眠状态uninterruptible sleep
S睡眠sleeping
T跟踪/停止traced or stopped
Z僵尸进程zombie

查看内存的占用情况

free -mh
命令组成含义
free查看内存的命令
mMB,表示转换为单位MB进行显示
hhuman,表示以人类可读的方式显示

输出内容如下

              total        used        free      shared  buff/cache   available
Mem:           7.6G        3.2G        3.2G        129M        1.2G        4.0G
Swap:          7.9G          0B        7.9G

具体含义如下

参数含义
total总内存
used使用内存
free空闲内存
shared共享内存
buff/cache缓冲区内存
available可用内存

查看磁盘的情况

df -h

输出内容

文件系统                 容量     已用  可用   已用% 挂载点
devtmpfs                 3.9G     0  3.9G    0% /dev
tmpfs                    3.9G     0  3.9G    0% /dev/shm
tmpfs                    3.9G  130M  3.7G    4% /run
tmpfs                    3.9G     0  3.9G    0% /sys/fs/cgroup

可以看磁盘是否出现沾满的情况

查看当前程序的进行ID

jps
#或者
jps -V

显示两列,分别是进程ID和启动类的名称

33809 RemoteJdbcServer
31428 RemoteMavenServer36
31286 
34573 Launcher
34574 CartoonApplication
35887 Jps

因为本次运行的服务是CartoonApplication,所以进程id为34574

查看进程的GC回收状态

jstat -gc 34574 2000
命令及参数含义
jstatjdk自带的工具
-gc查看gc的情况(还可以查看class/compile/gcold)
34574进程ID,本次要查看的进程ID为34574,生产上要根据上一步的jps获得
2000每隔2000毫秒打印一次

返回的结果如下所示:

image-20210815142738664

其中

参数全称释义
S0CCurrent survivor space 0 capacity (KB).S0的当前总容量(KB)
S1CCurrent survivor space 1 capacity (KB).S1的当前总容量(KB)
S0USurvivor space 0 utilization (KB).S0的利用情况(KB)
S1USurvivor space 1 utilization (KB).S1的利用情况(KB)
ECCurrent eden space capacity (KB).伊甸园的当前总容量(KB)
EUEden space utilization (KB).伊甸园的利用情况(KB)
OCCurrent old space capacity (KB).老年代的当前总容量(KB)
OUOld space utilization (KB).老年代的利用情况(KB)
PCCurrent permanent space capacity (KB).永久代的当前总容量(KB)
PUPermanent space utilization (KB).永久代的利用情况(KB)
YGCNumber of young generation GC Events.YoungGC的次数(KB)
YGCTYoung generation garbage collection time.YoungGC的时间
FGCNumber of full GC events.FullGC次数
FGCTFull garbage collection time.FullGC时间
GCTTotal garbage collection time.GC总时间

这里感觉官方的说法并不好,因为实际上Current capacity其实应该翻译为总容量的意思。

而utilization不应该翻译为利用率,而应该是使用情况。因为单位是KB。

我们在这个过程中最应该关心的就是 FGC 的次数

如果说我们每2秒打印一次,它的FullGC就增加1次。这就说明在这个过程中发生了频繁的GC

从上图可得,每2秒打印,它的 FGC 一直保持为2,说明从系统运行到现在为止总共只发生了2次GC

保存进程的栈现场

jstack 34574 > jstack.log
参数或命令含义
jstackjdk自带命令,可以查看栈现场的情况
34574进程ID,本次要查看的进程ID为34574,生产上要根据上一步的jps获得
>重定向
jstack.log重定向到当前目录下的jstack.log,若没有则生成

对刚刚生成的栈现场文件进行分析,查看有多少个线程

grep 'java.lang.Thread.State' jstack.log | wc -l
参数或命令含义
grep匹配并过滤出
‘java.lang.Thread.State’匹配并过滤出java.lang.Thread.State的文本内容
jstack.log被过滤的文件
管道,用于将过滤出的结果传递给下一个命令
wcword count 统计
-l按行统计,统计行数

输出结果

83

说明当前程序总共在跑的线程数有83个。事实上,83个线程也不是很多

查看线程是否有异样

grep -A 1 'java.lang.Thread.State' jstack.log | grep -v 'java.lang.Thread.State' | sort | uniq -c |sort -n

参数命令

参数含义
grep -A 1 ‘java.lang.Thread.State’匹配 java.lang.Thread.State并过滤文件,并关联出对应上下各1行
jstack.log被匹配的文件
管道,将刚刚的输出结果作为输入
grep -v ‘java.lang.Thread.State’匹配不含java.lang.Thread.State的内容(这样只剩下上下各一行)
sort将所有行按照字典序排序
uniq -c去除排序后相连重复的行
sort -n以数字的形式排序

输出

   1 	at java.lang.Thread.sleep(Native Method)
   1 	at java.net.PlainSocketImpl.socketAccept(Native Method)
   1 	at java.net.SocketInputStream.socketRead0(Native Method)
   1 	at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
   3 	at sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
  11 
  14 	at java.lang.Object.wait(Native Method)
  51 	at sun.misc.Unsafe.park(Native Method)
  82 --

看输出没有任何异样

保存进程的堆现场

jmap -dump:format=b,file=heap.log 34574
参数或命令含义
jmapjdk自带命令,可以查看堆现场的情况
-dump:format=b以二进制的形式输出堆
file=heap.log输出文件的文件名为heap.log
34574进程ID,本次要查看的进程ID为34574,生产上要根据上一步的jps获得

下载服务器上的堆文件到本地

服务器上没有好的可视化分析工具,必须把文件下载到本地

查看文件大小

ls -lh

显示内容,文件大概219MB

-rw-------  1 admin  staff   219M  8 15 15:09 heap.log

但是生产上的堆文件可能很大,所以我们需要先对文件内容进行压缩,然后再进行传输

gzip -6 heap.log

以下是参数的命令

参数或命令含义
gzip压缩命令
-6用于指定压缩等级,-1 压缩等级最低,压缩比最差;-9 压缩比最高。默认压缩比是 -6
heap.log被压缩的文件名

稍等片刻压缩完毕

ls -lh

我们发现源文件消失,压缩后的文件出现,只有48MB,压缩到了原来的21%,非常方便传输

-rw-------  1 admin  staff    48M  8 15 15:09 heap.log.gz

然后把文件转移到本地后再解压

gunzip heap.log.gz

使用MAT软件进行堆内存分析

MAT 是分析 Java 堆内存的利器,使用它打开我们的堆文件(将文件后缀改为 .hprof), 它会提示我们要分析的种类。

image-20210815153333901

大对象情况,看起来好像没有,最大的对象也就只有2.4MB。

查看每个类的情况

截屏2021-08-1515.34.54

最大的是char[],总共有7万多个对象,内存占到7MB左右

image-20210815153632256

看起来都很正常。

因为这是本地为了测试jdk的工具,下了正常应用程序的一个堆。为了测试才打开的。事实上说明该本地应用程序确实没有什么问题

但是思路是一样的,就是通过一步步的命令,层层排查,抽丝拨茧。

你如果有遇到什么线上排查成功的例子,也可以在评论区分析给大家

致谢

https://www.toutiao.com/i6698492096132678152/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1&timestamp=1628998274&app=news_article&utm_source=weixin&utm_medium=toutiao_android&use_new_style=1&req_id=202108151131140101512130844D7398DF&share_token=39c48a51-1e0e-49ce-8095-335479801aa8&group_id=6698492096132678152&wid=1628998991385

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值