【每日一题】聊一聊线上oom的排查方案?

Posted by 石福鹏 on 2020-09-23

oom,先说排查思路,oom是内存溢出,则有

  • 堆内存区域溢出
  • 方法区或者元数据区溢出
  • 非堆空间(DirectMemory)溢出

三种场景的情况。 首先通过溢出的异常报错,先确认是哪种类型。

  1. 如果确认是堆内存区域溢出,可能原因是内存泄露 或者堆空间回收的速度赶不上对象创建的速度,排查方案就是看gc日志,如果是gc频率很高 但是回收对象却很少 就要怀疑是内存泄露,这个时候看dump日志 找到里面占用最多的实体对象,然后再排查代码 为什么这个没回收。
    如果是gc频繁,对象也能成功大量回收,但是依然溢出,说明堆空间大小跟不上业务场景的对象创建速度,要么扩大堆空间,要么优化业务场景减少这类对象的产生,为了确认是哪个对象大量产生 也需要jmap 分析dump日志找出是哪个对象,然后看代码针对性优化。
  2. 方法区或者元数据区溢出,可能是方法区或者元数据区过小,也可能是类过多,需要结合项目里面是否使用了会大量生成类的框架,比如说cglib asm 等,典型的就是aop框架大量生成代理, 可以使用命令jmap –hisoty more命令查看是哪个类最多。
  3. 3.直接内存溢出, 出现在使用unsafe.allocate直接分配内存或者项目里面使用了nio或者nio框架,排查情况比较复杂,原因也有内存泄露 代码不合理等,需要具体情况具体分析,直接内存溢出的典型特征就是dump下来的内存文件很小,但是进程占用的内存空间却很大。

利用的工具和命令有 ps 查找进程ID,jmap dump堆内存文件和分析堆内存jstat查看Java进程信息 包括gc 在内


1、通过命令查看对应的进程号 比如:jps 或者 ps -ef | grep servicemix
2、输入命令查看gc情况 命令:jstat -gcutil进程号 刷新的毫秒数 展示的记录数
比如:jstat -gcutil 14050 1000 10 (查看进程号14050,每隔1秒获取下,展示10条记录)
3、查看具体占用情况:
(1)命令: jmap -histo 进程号 | more (默认展示到控制台)
(2)命令: jmap -histo 14050 | more > exceptionlog.txt (输出到当前目录的exceptionlog.txt文件)
比如:jmap -histo 14050 | more 查看具体的classname,是否有开发人员的类,也可以输出到具体文件分析