内存溢出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


### 下载 Popper.min.js 文件的方法 对于希望获取 `popper.min.js` 的开发者来说,可以通过多种方式来实现这一目标。通常情况下,推荐通过官方渠道或可靠的分发网络 (CDN) 来获得最新的稳定版文件。 #### 使用 CDN 获取 Popper.min.js 最简单的方式之一是从流行的 CDN 中加载所需的 JavaScript 库。这不仅简化了集成过程,还可能提高性能,因为许多用户已经缓存了来自这些服务提供商的内容。例如: ```html <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2/dist/umd/popper.min.js"></script> ``` 这种方式不需要手动下载文件到本地服务器;只需将上述 `<script>` 标签添加至 HTML 文档中的适当位置即可立即使用 Popper 功能[^1]。 #### 从 npm 或 yarn 安装 如果项目采用模块化构建工具链,则可以直接利用包管理器如 npm 或 Yarn 进行安装。命令如下所示: ```bash npm install @popperjs/core # 或者 yarn add @popperjs/core ``` 之后可以根据具体需求引入特定功能模块,而不是整个库,从而减少打包后的体积并优化加载速度[^2]。 #### 访问 GitHub 发布页面下载压缩包 另一种方法是访问 Popper.js 的 [GitHub Releases](https://github.com/popperjs/popper-core/releases) 页面,在这里可以选择不同版本的 tarball 或 zip 归档进行下载解压操作。这种方法适合那些偏好离线工作环境或是想要定制编译选项的人群[^3]。 #### 手动克隆仓库 最后一种较为少见但也可行的办法便是直接克隆完整的 Git 存储库副本。这样可以获得开发分支以及历史记录等更多信息,适用于贡献代码或者深入学习内部机制的情况。 ```bash git clone https://github.com/popperjs/popper-core.git cd popper-core ``` 完成以上任一途径后便能成功取得所需版本的 Popper.min.js 文件,并将其应用于个人项目之中[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值