ipmitool查看内存信息_Java内存泄漏和CPU飙高问题排查全流程

基础知识点

Linux命令

top - display Linux processes

top -p 452     // 查看指定进程452的CPU内存信息top -H -p 452  // 查看指定进程452的所有线程的CPU内存信息

虚拟机工具

  • jps:查询虚拟机进程
  • jstat:用于查看GC日志。-gc, -gcutils。jstat -gc 452 250 20 // 每250毫秒查询一次进程452垃圾收集状况,一共查询20次
  • jmap:用于生成堆转储快照(heapdump文件)。通过启动时添加-XX:+HeapDumpOnOutOfMemoryError参数,可以让虚拟机在内存溢出异常出现之后自动生成堆转储快照文件。在Linux系统下通过Kill-3命令发送进程退出信号“恐吓”一下虚拟机,也能顺利拿到堆转储快照。jmap -dump:format=b,file=height-cpu.bin 2388 //生成堆转储快照
  • jhat:用来分析jmap生成的堆转储快照,一般不用,一般用VisualVM,Eclipse MemoryAnalyzer、IBM HeapAnalyzer等工具。
  • jcmd: 用于生成堆直方图,通过堆直方图可以快速看到应用内的对象数目,同时不需要进行完整的堆转储(因为堆转储需要一段时间来分析,而且会消耗大量磁盘空间)。
  • jstack:用于生成虚拟机当前时刻的线程快照(一般称为threaddump或者javacore文件)。通常用来定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间挂起等。

堆转储文件

堆转储文件(heap dump)是某个时间点Java 进程的内存快照。包含了当时内存中还没有被 full GC 回收的对象和类信息。因为堆转储文件没有保存共享信息,所以找不到对象创建者信息。

线程快照

线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合。线程出现停顿时通过jstack来查看各个线程的调用堆栈,就可以获知没有响应的线程到底在后台做些什么事情,或者等待着什么资源。

GC日志

CPU百分百问题如何排查

CPU百分百的重现

@SpringBootApplication@RestControllerpublic class CpuApplication {@RequestMapping("/")String home() {    while(true) {    System.out.println("一刻不停的处理任务");   // 行号是15    }}public static void main(String[] args) {    SpringApplication.run(CpuApplication.class, args);}}

创建一个SpringBoot项目,启动运行,如果没有请求进来时,CPU是正常的。 通过jps命令查看CpuApplication的进程ID是452,通过top -p 452,可以看到当前的CPU使用率是0.3%。

c972c134d5bf07699dcbbd2b7dc6d2ce.png

访问http://localhost:8080,再次通过top -p 452来查看CPU使用率,可以看到CPU使用率飙升,超过90%。

ef493b3aa7d50ca4cce9ac90e995f909.png

问题排查流程

使用top -H -p 452可以查看进程的所有线程CPU使用率,来确认是那个线程引发的问题。

ac83331c9378131c6cae0a31920acb21.png

接下来把线程的pid 475转成16进制。通过在线进制转换工具进行转换,475转成1db。 之后就可以通过jstack -l 452 | grep 1db -A 74打印线程475的快照。

32c7334c13270bcdd20f875f33eac76d.png

从图中可以看到线程一直处于运行状态,执行代码的位置是CpuApplication.home方法的第15行。 查看代码可以知道代码里面while循序一直在处理任务,没有让出CPU,导致CPU飙升。

优化办法

  • 循环要有结束条件,避免死循环,当然对于不断有任务需要处理的调度程序来说是避免不了的。
  • 每次处理完任务之后休息一会,休息的时间最好做成可配置的,可以根据CPU的使用率进行动态调整。
  • 修改代码如下
@SpringBootApplication@RestControllerpublic class CpuApplication {@RequestMapping("/")String home() {    while(true) {    System.out.println("一刻不停的处理任务");    try {    // 每次任务处理完成之后,休息100毫秒。    TimeUnit.MILLISECONDS.sleep(100);    } catch (InterruptedException e) {    e.printStackTrace();    }    }}public static void main(String[] args) {    SpringApplication.run(CpuApplication.class, args);}}

优化之后,访问http://localhost:8080,通过top命令查看CPU使用率一直很稳定,在1%到2%之间。

370b0f0bcb0cfd47e012b46f1341e614.png

内存泄漏问题如何排查

内存泄漏的重现

@SpringBootApplication@RestControllerpublic class CpuApplication {@RequestMapping("/")String home() {    List list = new ArrayList();    int i = 0;    while(true) {    System.out.println("一刻不停的处理任务");    list.add(new String(new byte[1024 * 1024]) + "处理任务分配一个1M的对象,序号为 " + i);    i++;    try {    // 每次任务处理完成之后,休息100毫秒。    TimeUnit.MILLISECONDS.sleep(100);    } catch (InterruptedException e) {    e.printStackTrace();    }    }}public static void main(String[] args) {    SpringApplication.run(CpuApplication.class, args);}}

程序启动之后,访问http://localhost:8080一段时间后,发生java.lang.OutOfMemoryError错误,错误日志见下图:

5aaa4a323fc0f84133e671edfb4b21b2.png

问题排查流程

  • 首先通过jps查看进程PID是2785,然后通过top -p 2785发现内存升高到28.1%。
  • 多次访问http://localhost:8080,多次使用jstat -gc 2785查看GC日志
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU          YGC     YGCT    FGC    FGCT      GCT34048.0 34048.0  0.0   33878.1 272640.0 236265.6  732416.0   44799.7   37116.0 35385.8 4860.0 4567.0    335    4.208  138     5.299    9.507

可以发送OU的内存,也就是老年代的使用内存在一直增加,有对象一直处于存活,并且一直有新对象产生。接下来就通过堆栈储文件查看内存的使用情况。

  • 再次访问http://localhost:8080,通过jmap -dump:format=b,file=height-cpu.bin 2785生产堆转储快照文件。
  • 使用Eclipse的内存分析器工具(MAT)打开height-cpu.bin文件进行堆内存分析。通过上面Overview页面的饼图,可以发现一个对象占了96%以上的内存。
  • 打开leak suspects页面,可以查看内存泄漏的原因。可以看到一个Object[]对象占了96.26%的内存。
  • 点击详情可以查看具体的对象
c22c1e26066d7826558ff1672a0dbe27.png

从详情页可以看到是ArrayList对象是内存聚集点,内存一直没有释放,如果熟悉代码,就可以找到问题点,就是我们新增的ArrayList一直没有释放,导致堆内存溢出。

参考链接

  1. https://www.cnblogs.com/lyhero11/p/11970924.html
  2. https://www.javatang.com/archives/2017/10/19/33151873.html
  3. https://www.cnblogs.com/duanxz/p/3958504.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值