一.线上内存相关问题排查
关注点:内存溢出、内存分配不合理、对象不断创建导致
1.查看当前 java 应用的进程号
指令:ps -ef|grep java 或者 jps 命令
在jdk的bin路径下找到jps命令,如果输入jps提示bash: jps: command not found 就需要带上全路径:
/usr/local/jdk1.8.0_92/bin/jps
复制代码
jps 实际执行效果:
2.使用 jmap 查看进程的内存情况(需要 root 权限执行)
指令:jmap -heap 8931
作用:可以查看新生代,老生代堆内存的分配大小以及使用情况
指令:/usr/local/jdk1.8.0_92/bin/jmap -heap 8931
复制代码
实际执行效果如图:
上图中各个参数的解释如下:
using parallel threads in the new generation. ##新生代采用的是并行线程处理方式
using thread-local object allocation.
Concurrent Mark-Sweep GC ##同步并行垃圾回收
Heap Configuration: ##堆配置情况
MinHeapFreeRatio = 40 ##最小堆使用比例
MaxHeapFreeRatio = 70 ##最大堆可用比例
MaxHeapSize = 2147483648 (2048.0MB) ##最大堆空间大小
NewSize = 268435456 (256.0MB) ##新生代分配大小
MaxNewSize = 268435456 (256.0MB) ##最大可新生代分配大小
OldSize = 5439488 (5.1875MB) ##老生代大小
NewRatio = 2 ##新生代比例
SurvivorRatio = 8 ##新生代与suvivor的比例
PermSize = 134217728 (128.0MB) ##perm区大小
MaxPermSize = 134217728 (128.0MB) ##最大可分配perm区大小
Heap Usage: ##堆使用情况
New Generation (Eden + 1 Survivor Space): ##新生代(伊甸区 + survior空间)
capacity = 241631232 (230.4375MB) ##伊甸区容量
used = 77776272 (74.17323303222656MB) ##已经使用大小
free = 163854960 (156.26426696777344MB) ##剩余容量
32.188004570534986% used ##使用比例
Eden Space: ##伊甸区
capacity = 214827008 (204.875MB) ##伊甸区容量
used = 74442288 (70.99369812011719MB) ##伊甸区使用
free = 140384720 (133.8813018798828MB) ##伊甸区当前剩余容量
34.65220164496263% used ##伊甸区使用情况
From Space: ##survior1区
capacity = 26804224 (25.5625MB) ##survior1区容量
used = 3333984 (3.179534912109375MB) ##surviror1区已使用情况
free = 23470240 (22.382965087890625MB) ##surviror1区剩余容量
12.43827838477995% used ##survior1区使用比例
To Space: ##survior2 区
capacity = 26804224 (25.5625MB) ##survior2区容量
used = 0 (0.0MB) ##survior2区已使用情况
free = 26804224 (25.5625MB) ##survior2区剩余容量
0.0% used ## survior2区使用比例
concurrent mark-sweep generation: ##老生代使用情况
capacity = 1879048192 (1792.0MB) ##老生代容量
used = 30847928 (29.41887664794922MB) ##老生代已使用容量
free = 1848200264 (1762.5811233520508MB) ##老生代剩余容量
1.6416783843721663% used ##老生代使用比例
Perm Generation: ##perm区使用情况
capacity = 134217728 (128.0MB) ##perm区容量
used = 47303016 (45.111671447753906MB) ##perm区已使用容量
free = 86914712 (82.8883285522461MB) ##perm区剩余容量
35.24349331855774% used ##perm区使用比例
复制代码
3.找到最耗内存的对象
指令:jmap -histo:live 8931|less
作用:可以很方便的看到实例数、占用内存大小、全类名
注意:线上执行该命令会强制执行一次 fgc
指令:/usr/local/jdk1.8.0_92/bin/jmap -histo:live 8931|less
顺带复习less指令:
less 是一个Linux命令行实用程序,用于显示文件或命令输出的内容,它一次只显示一个页面。它类似于 more ,但具有更高级的功能,允许您在文件中向前和向后导
航。该 less 命令主要用于打开大文件。less 不会读取整个文件,相比于 vim 或 nano 等文本编辑器,加载时间会更快。
-m 显示读取文件的百分比;
-i 忽略搜索时的大小写
b:是上翻一页
空格:下翻一页
回车:是下一行
q:退出less
/字符串:搜索"字符串"的功能
举例:
ps -ef | grep java | less(分页查看java进程)
ps -ef | less(分页查看进程)
less log2013.log(查看日志文件)
less log2013.log log2014.log(查看多个文件)
复制代码
实际执行效果如下图所示:
4.还可以通过 dump 出堆内存信息进行分析
指令:jmap -dump:format=b,file=jmapdump_all 进程号
执行一下语句:
/usr/local/jdk1.8.092/bin/jmap -dump:format=b,file=jmap_dump_all 8931
执行完后会在当前目录出现一个二进制文件
复制代码
如下图所示二进制文件无法直接打开,需要通过工具进行分析,如果文件比较小,推荐使用 jdk 自带的 jvisiualvm 工具进行打开分析,如果文件较大推荐用 eclipse 的内存分析工具 MAT。
Eclipse Memory Analyzer(MAT)支持两种安装方式,一是 Eclipse 插件的方式,另外一个就是独立运行的方式,建议使用独立运行的方式。下载地址: http://www.eclipse.org/mat/downloads.php
jvisiualvm的使用方法暂时可参考:http://www.xwood.net/_site_domain_/_root/5870/5874/t_c279949.html
MAT的使用方法:(推荐)https://blog.csdn.net/itomge/article/details/48719527、https://www.jianshu.com/p/2cf7169ba1c4?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
、https://www.cnblogs.com/hanlinhu/p/10174185.html
复制代码
5.查看句柄和线程使用情况
查看句柄详情:ll /proc/9339/fd
查看线程详情:ll /proc/9339/task
当前使用的句柄数量: ll /proc/${PID}/fd | wc -l
当前使用的线程数量:ll /proc/${PID}/task | wc -l
顺便复习wc的用法:
wc -c filename:显示一个文件的字节数
wc -m filename:显示一个文件的字符数
wc -l filename:显示一个文件的行数
wc -L filename:显示一个文件中的最长行的长度
wc -w filename:显示一个文件的字数
复制代码
查看当前进程使用句柄数量
查看当前进程使用线程数量
6.linux 系统句柄数查看和配置
1.查看句柄数量
指令:ulimit -n
默认值:1024
2.查看当前各个进程句柄使用情况
指令:lsof -n|awk '{print $2}'|sort|uniq -c|sort -nr|more
复制代码
实际执行效果
3.修改句柄大小
修改文件:/etc/security/limits.conf,在文件中添加
soft nofile 32768 #限制单个进程最大文件句柄数(到达此限制时 系统报警)
hard nofile 65536 #限制单个进程最大文件句柄数(到达此限制时系统报错)
修改文件:/etc/sysctl.conf。在文件中添加:
fs.file-max=655350 #限制整个系统最大文件句柄数
复制代码
6.总结
1.jps 查看进程号
2.jmap -heap 进程号 查看 java 进程堆内存分配情况
3.jmap -histo:live 进程号 | less 查看当前存活的最耗内存的对象
4.也可以通过 jmap -dump:format=b,file=jmapdump_all 进程号 dump 出内存镜像,通过 MAT 分析
5.查看当前进程使用的句柄数:ll /proc/进程号/fd | wc -l
查看当前进程使用的线程数量:ll /proc/进程号/task | wc -l
6.查看 linux 系统句柄数: ulimit -n
二.线上服务器 cpu 负载高
1.通过 top 命令可以查看当前 cpu,内存使用情况
第一列为当前进程的进程 id,通过 top 很容易找到 cpu 使用较高的进程。如果确定是 java 应用导致的 cpu 高,也可以通过 jdk 自带的 jps 命令查看 java 进程的进程号。
2.找到进程下耗资源的线程信息
指令:top -p 8931 -H
第一列为进程下的线程的 pid,可以看到线程 id 为 9996
3.导出进程的栈信息
指令:jstack 8931 >> stack.log
指令:/usr/local/jdk1.8.0_92/bin/jstack 8931>>stack.log
复制代码
接下来在当前目录会出现一个 stack.log 文件
4.将 cpu 消耗高的线程的 pid 换算为 16 进制
指令:printf 0x%x 9996
得到 16 进制值为:0x270c
5.从刚才的栈日志中查找该线程正在运行的方法
指令:grep 0x270c stack.log -a 10
如果不想dump出stack.log文件,那么也可以直接在服务器查看:jstack 17850|grep 0x270c -a 10
另外也可以查找正在运行的线程,及线程处于运行状态的位置,从这些线程中来查找资源消耗过高的代码。
指令:grep RUNNABLE stack.log -a1
复制代码
6.总结
1.通过 top 命令找到占用 cpu 高的进程
2.通过 top -p 进程号 -H 找到进程下的线程使用情况
3.导出堆栈信息 jstack 进程号 >> stack.log
4.将最耗 cpu 的线程 id 转成 16 进制,printf 0x%x 9996
5.在 stack.log 中查看该线程是哪里在使用,分析定位原因
三.其他有用的排查问题指令
1.ps -efL, 显示所有活跃线程 ID 和对应时间
pid 为进程 id
ppid 为线程 id
往后翻可以看到 java 各个线程的存活情况
2.查看某一个端口的使用方式:
3.
评论