内存溢出OOM故障定位实践

最近负责项目的防火墙,陆陆续续查到了好几个OOM的故障,记录下。

分析OOM最直接的就是分析dump文件,从dump文件中,可以看出究竟是谁在光吃不吐。。

第一种:

推荐一个分析dump的工具MemoryAnalyzer,可以以eclipse插件运行,也可以单独运行。

Eclipse提供的EMA工具,可以帮助进行内存的分析。主要是分析dump文件,则第一步即获取dump文件。

可以手动获取,或配置在java程序超过配置内存时,自动生成dump文件。

 

下载:MemoryAnalyzer-1.7.0.20170613-win32.win32.x86_64

 

修改工具中MemoryAnalyzer.ini-Xmx7024m需要配置为dump文件大小

1.通过该工具EMA获取dump文件


2.通过工具JConsole获取dump文件


3.配置后台程序,启动参数,在内存溢出时,自动输出dump文件

VM options:

-Xmx10m(分配最大内存)

-XX:+HeapDumpOnOutOfMemoryError(程序内存溢出自动转存dump)

-XX:+HeapDumpOnCtrlBreak(手动退出时,转存dumpjava1.6已经取消)

-XX:HeapDumpPath=D:\sso\dump(转成dump文件路径)


4.命令获取

Jps获取java程序pid 32652

jmap -dump:format=b,file=D:/sso/dump/32652.hprof 32652


===============

简单介绍下例子,复杂应用,可以参考EMA的pdf文档


为使内存溢出,尽快出现,可以通过-Xmx60m 设置分配给该Java程序的最大内存,注意至少加上-XX:+HeapDumpOnOutOfMemoryError

package com.zte.sunquan.demo.oom;
import com.zte.sunquan.demo.la.User;
import java.util.ArrayList;
import java.util.List;
public class OOMTest {
    static List<User> list = new ArrayList<>();
    public static void main(String[] args) {
        while (true) {
            User user = new User("sunquan", 12);
            list.add(user);
            System.out.println(list.size());
        }
    }
}

使用EMA打开生成的dump文件



在打开之前,需要注意是,EMA的使用 内存与dump文件大小有关系,在定位8G多的dump文件时,我的PC机十分的卡,同时需要设置EAM的配置:


修改文件MemoryAnalyzer.ini中内存大小

-startup
plugins/org.eclipse.equinox.launcher_1.3.100.v20150511-1540.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.300.v20150602-1417
-vmargs
-Xmx7024m


工具会自动产生怀疑内存泄露的地方,可以定位到类,如果这时你能找到对应的开发,这个问题基本上就能解决了。


dominator_tree支配树,用于描述对象关系图,可以通过对象引用,容易的找到占用内存最大的一块以及对象的依赖关系,十分有用。


一方面,可以看到共产生了810300个User对象,且存在数组中。

另一方面:

shallow heap指对象自身在堆内存中的大小

retained heap 指对象被垃圾回收后能释放的内存大小


此外查询怀疑对象的引用关系,两个概念:

Incomming Reference:引用当前对象的的外部对象
Outgoing Reference:当前对象引用的外部对象


一般通过incomming reference,都能找到调用处,从而定位到内存泄露的源头。


histogram直方图,用于查询各个类的对象的大小、数据


以上方法,已经可以解决多数OOM问题了


第二种:

在centos中系统,某个JAVA线程被莫名其妙地杀死了,并且没有dump文件,一开始也不一定定位出是OOM,所以只有查看操作系统的日志

进行/etc/log/messages中查看

Dec  7 01:31:08 localhost kernel: mongod invoked oom-killer: gfp_mask=0x200da, order=0, oom_score_adj=0

.......

Dec  7 01:31:11 localhost kernel: Out of memory: Kill process 3574 (java) score 608 or sacrifice child
Dec  7 01:31:11 localhost kernel: Killed process 3574 (java) total-vm:16288452kB, anon-rss:9314544kB, file-rss:0kB

很明显操作系统的oom-killer启动,杀掉了java进程,同时该进程在死前,占用了将近8G的内存 (本就分配了8G,从这点才能判断出是OOM)


接下来,继续定位,则显的十分困难,无dump文件,程序也退出了。可以从日志中寻找线索,或者重启程序,手动取dump分析


第三种:

2017-12-09 13:40:12,739 | WARN  | oupCloseable-9-5 | DefaultChannelPipeline           | 107 - io.netty.common - 4.1.8.Final | An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
java.lang.OutOfMemoryError: unable to create new native thread
	at java.lang.Thread.start0(Native Method)[:1.8.0_121]
	at java.lang.Thread.start(Thread.java:714)[:1.8.0_121]

2017-12-09 13:40:32,633 | ERROR | ult-dispatcher-2 | ActorSystemImpl                  | 381 - com.typesafe.akka.slf4j - 2.4.8 | Uncaught error from thread [opendaylight-cluster-data-akka.remote.default-remote-dispatcher-7] shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled
java.lang.OutOfMemoryError: unable to create new native thread
	at java.lang.Thread.start0(Native Method)[:1.8.0_121]
	at java.lang.Thread.start(Thread.java:714)[:1.8.0_121]

上图的日志中,可以看到也是一种OOM,这种OOM,主要是由于创建的线程过多,同时不释放导致的。系统内存是有限的(线程占用的内存,主要在系统内存去掉分配给JAVA内存后的那部分内存),换种表述,即在系统内存一定的情况下,分配给JAVA内存越多,所能创建的线程其实越小。

用可分配内存/325KB,大致可以计算出可以创建的线程数,如系统12G内存,JAVA用了8G,其它的用了1G,那就剩下12-8-1=3G,3G/325K=9679,即大约可以同时创建9679个线程


这种故障,个人感觉多数还是代码不规范,不合理地使用线程,可以结合日志进行查询。当然如果,内存明明还剩余很多,可能是由于操作系统本身对于线程数据的限制,可以

进行修改,如下:


切换到对应用户中su XXX

ulimit -u 不是-n

具体限制数值,在etc/security/limit.d/20-nproc.conf



上图,表述除root用户外,其余用户只允许创建4096个线程,如果程序需要确实大于这个数目,可以适当调用,或新增对应用户的配置。

https://www.cnblogs.com/myshare/archive/2016/02/02/5177135.html


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值