背景
作为java主要开发人员,同时兼具项目部署在工作中难免遇到服务器资源,特别是内存消耗优化的问题,日常之谈,遇到了就会加上来,更多详细的信息可以参考阿里专业jvm调优大神的公众号‘你假笨’.
第一步使用top命令查看CPU占用率
centos中分析java占用大量内存的java代码,命令如下。
1:在命令行提示符执行top命令
2:输入大写P,则结果按CPU占用降序排序。输入大写M,结果按内存占用降序排序。(注:大写P可以在capslock状态输入p,或者按Shift+p)
PID(Process ID):进程标示号 ( 每个 process 的 ID )
USER:进程所有者的用户名 ( 该 process 所属的使用者 )
PR:进程的优先级别 ( Priority 的简写,程序的优先执行顺序,越小越早被执行 )
NI:进程的优先级别数值 ( Nice 的简写,与 Priority 有关,也是越小越早被执行 )
VIRT:进程占用的虚拟内存值。
RES:进程占用的物理内存值。
SHR:进程使用的共享内存值。
S:进程的状态,其中S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值是负数。
%CPU:该进程占用的CPU使用率。
%MEM:该进程占用的物理内存和总内存的百分比。
TIME+:该进程启动后占用的总的CPU时间 ( CPU 使用时间的累加
主要关注的字段,PID,RES,%CPU,%MEM
可以根据看到pid
根据PID可以查询cpu和内存使用的具体PID
根据PID找到CPU占用最高的TID并转换成16进制
根据第步找到该进程后,就要定位具体线程或代码,首先显示线程列表,并按照CPU占用高的线程排序:
ps -mp 16871 -o THREAD,tid,time | sort -rn
显示结果如下:
USER %CPU PRI SCNT WCHAN USER SYSTEM TID TIME
root 90.5 19 – – – – 14065 01:16:48
root 90.1 19 – – – – 14067 01:02:16
找到了耗时最高的线程9521,占用CPU时间有1个多小时了。frown
将需要的线程ID转换为16进制格式:
printf “%x\n” 14065
36f1
最后PID,TID使用jvm命令打印线程的堆栈信息:
jstack 16871 |grep 36f1 -A 30
这样就找到占用java,cpu资源的代码了,接下来还得分析代码执行的原因了。
"GC task thread#0 (ParallelGC)" prio=10 tid=0x00007fa35001e800 nid=0x36f1 runnable
"GC task thread#1 (ParallelGC)" prio=10 tid=0x00007fa350020800 nid=0x36f2 runnable
"GC task thread#2 (ParallelGC)" prio=10 tid=0x00007fa350022800 nid=0x36f3 runnable
"GC task thread#3 (ParallelGC)" prio=10 tid=0x00007fa350024000 nid=0x36f4 runnable
"GC task thread#4 (ParallelGC)" prio=10 tid=0x00007fa350026000 nid=0x36f5 runnable
"GC task thread#5 (ParallelGC)" prio=10 tid=0x00007fa350028000 nid=0x36f6 runnable
"GC task thread#6 (ParallelGC)" prio=10 tid=0x00007fa350029800 nid=0x36f7 runnable
"GC task thread#7 (ParallelGC)" prio=10 tid=0x00007fa35002b800 nid=0x36f8 runnable
"VM Periodic Task Thread" prio=10 tid=0x00007fa3500a8800 nid=0x3700 waiting on condition
JNI global references: 392
GC task thread (ParallelGC)可以看出是堆内存占用率过高,ParallelGC是垃圾回收器的类型是用来给新生代做GC的。
使用jstat -gcutil命令查看进程的内存情况
打印TID的堆栈快照
[ylp@ylp-web-01 ~]$ jstat -gcutil 14063 2000 10
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 0.00 100.00 99.99 26.31 42 21.917 218 1484.830 1506.747
0.00 0.00 100.00 99.99 26.31 42 21.917 218 1484.830 1506.747
0.00 0.00 100.00 99.99 26.31 42 21.917 219 1496.567 1518.484
0.00 0.00 100.00 99.99 26.31 42 21.917 219 1496.567 1518.484
0.00 0.00 100.00 99.99 26.31 42 21.917 219 1496.567 1518.484
0.00 0.00 100.00 99.99 26.31 42 21.917 219 1496.567 1518.484
0.00 0.00 100.00 99.99 26.31 42 21.917 219 1496.567 1518.484
0.00 0.00 100.00 99.99 26.31 42 21.917 220 1505.439 1527.355
0.00 0.00 100.00 99.99 26.31 42 21.917 220 1505.439 1527.355
0.00 0.00 100.00 99.99 26.31 42 21.917 220 1505.439 1527.355
从输出信息可以看出,Eden区内存占用100%,Old区内存占用99.99%,Full GC的次数高达220次,并且频繁Full GC,Full GC的持续时间也特别
分析堆栈
使用jstat命令查看进程的堆栈情况,需要把快照保存到本地
jstat 14063 >>jstat.out
拿到本地后,用编辑器查找带有项目并且线程状态是RUNABLE的相关的堆栈信息,从图中可以看出ActivityUtil.java类的447行正在使用HashMap.put()方法
修改或者优化代码逻辑,重新打包发布版本,使用相同条件重测。