最近负责项目的防火墙,陆陆续续查到了好几个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(手动退出时,转存dump,java1.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 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文件大小有关系,在定位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
接下来,继续定位,则显的十分困难,无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]
用可分配内存/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