<一>试题1:某服务器CPU使用率达到99%,排查是哪个程序的哪个线程导致的高CPU。
思路:
1.1、先找到耗CPU高的进程;
1.2、找到耗CPU高的线程;
1.3、找到耗CPU高的线程对应的业务代码;
操作:
1.1、执行“top -c”命令,显示进程运行信息列表,键入大写P,按CPU使用率降序排列:
1.2、获取到进程PID为10765的进程,使用CPU资源最高19.9%;
至此,已找到耗CPU最高的进程,进程PID为10765,后续命令中需要使用到。
2.1、一个进程内有很多线程,执行“top -Hp 10765”,显示进程ID为10765的线程列表,键入大写P后,按CPU使用率降序排列:
2.2、其中,PID为10804的线程,CPU使用率最高2.3%;
2.3、将线程ID(10804)按16进制展示,执行指令“printf "%x\n" 10804”:
至此,找到了CPU使用率最高的线程ID为10804,并获取到10804的16进制标识:2a34
(转为16进制,是因为jstack打印出的线程栈信息中,线程id是通过16进制展示的)
3.1、通过jstack检索到进程(进程ID=10765)中,最耗CPU的线程(线程ID=2a34)的线程栈信息;
执行指令“jstack 10765 | grep "2a34" -C5 --color”:
至此,找到了耗CPU高的线程对应的线程名称“AsyncLogger-1”,而这个线程名称是我们业务代码中给线程取的名称,可以快速定位到业务代码。
tips:给线程取一个与业务处理相关的名称,对快速定位问题尤为重要。
如果我们没有给线程取名称“AsyncLogger-1”,那打印出来的线程名称可能是:
通过不知名的线程名称“pool-5-thread-1”,以及只包含jdk代码的线程栈信息,我们无法定位到业务代码。
<二>试题二:某java应用大量消耗内存,导致OutOfMemoryError
思路:
1、什么对象消耗内存最大;
2、是否创建了太多的线程;
3、新生的、老年代现在内存使用情况,确认是不是整体内存分配太小了;
4、实时查看新生的、老年代内存使用情况,GC情况
5、代码层检查,是否有大对象创建?需要调用close()或dispose()来回收的资源是否回收了?
操作:
1、执行“jmap -histo:live 10765 | more”命令,以表格的方式显示存活对象的信息(已按对象所占bytes大小进行降序排列):
其中,10765为进程ID,更多用法,通过“man jmap”寻求帮助。
占内存最多的对象类型是org.apache.logging.log4j.core.async.RingBufferLogEvent,
总共18874368byte (18M),例子中属于正常使用。
tips:如果发现某类对象占用内存很大(几个G的大小),很可能是有问题的。
基本都是因为:该类对象创建太多,且一直未释放。比如:使用完IO资源后,未调用close()接口关闭、释放资源;又比如:消费者消费速度慢(或停止消费了),而生产者不断往队列中投递任务,导致队列中任务累积过多,任务对象占用内存太多而产生OutOfMemoryError;
2、执行“pstree -p 10765 | wc -l”,查看进程内的线程数
其中,10765为进程ID。
每个线程需要分配线程栈内存,创建线程太多,可能导致OutOfMemoryError。
3、执行“jmap -heap 10765”,查看堆(新生代、老年代)内存分配大小及使用情况
4、执行“jstat -gc 10765 1000”,查看各个区内存使用情况及GC情况
其中,10765为进程ID,1000为数据刷新间隔的毫秒数
具体字段含义,通过“man jstat”寻求帮助。
主要查看:
EC:Eden区容量,EU:Eden区已使用量,OC:Old区容量,OU:Old区已使用量;
YGC:YongGC次数,YGCT:YongGC耗时,FGC:FullGC次数,FGCT:FullGC耗时;