问题排查之OOM (非原创,来自于同事的邮件分享)

标签: 问题 oom 原创 | 发表时间:2013-01-10 10:08 | 作者:
出处:http://fufeng.iteye.com

非原创,来自于同事的邮件分享。

前段时间在测试过程中发现了mina 框架的问题:当mina 一次传输的文件超过一定值(如55m )或者连续传输文件的次数过于频繁,就会内存溢出:

org.apache.mina.filter.codec.ProtocolEncoderException: java.lang.OutOfMemoryError: Java heap space

at org.apache.mina.filter.codec.ProtocolCodecFilter.filterWrite(ProtocolCodecFilter.java:217)

at org.apache.mina.common.support.AbstractIoFilterChain.callPreviousFilterWrite(AbstractIoFilterChain.java:361)

at org.apache.mina.common.support.AbstractIoFilterChain.access$1300(AbstractIoFilterChain.java:53)

at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.filterWrite(AbstractIoFilterChain.java:659)

at org.apache.mina.common.support.AbstractIoFilterChain$TailFilter.filterWrite(AbstractIoFilterChain.java:587)

at org.apache.mina.common.support.AbstractIoFilterChain.callPreviousFilterWrite(AbstractIoFilterChain.java:361)

at org.apache.mina.common.support.AbstractIoFilterChain.fireFilterWrite(AbstractIoFilterChain.java:355)

at org.apache.mina.transport.socket.nio.SocketSessionImpl.write0(SocketSessionImpl.java:166)

at org.apache.mina.common.support.BaseIoSession.write(BaseIoSession.java:177)

at org.apache.mina.common.support.BaseIoSession.write(BaseIoSession.java:168)

at com.taobao.forest.server.DefaultPushTimeTask.pushcachetothesession(DefaultPushTimeTask.java:441)

1 ) 开始是尝试用常规方法试图分析mina 在内存溢出时什么东东占了那么多内存还无法释放,于是在jboss 启动参数那加了两个参数 -XX:HeapDumpPath=\tmp  -XX:+HeapDumpOnOutOfMemoryError , 作用是在发生OutOfMemoryError 时将当时的内存映像dump 到/tmp 下,然后将dump 出来的内存映像文件下到本地用mat 分析,不过分析结果未发现有内存溢出问题,甚是奇怪。

2 )之后,又上网查了些资料,才发现mina 不是用的堆内存(Heap ),而是使用的本机直接内存(Direct Memory )

所谓本地直接 内存 并不是虚拟机运行时数据区的一部分,它根本就是本机 内存 而不是VM 直接管理的区域。

在JDK1.4 中新加入了 NIO 类,引入一种基于渠道与缓冲区的I/O 方式,它可以通过本机Native 函数库直接分配本机 内存 ,然后通过一个存储在Java 堆里面的DirectByteBuffer 对象作为这块 内存 的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java 对和本机堆中来回复制数据。显然本机直接 内存 的分配不会受到Java 堆大小的限制,但是即然是 内存 那肯定还是要受到本机物理 内存 (包括SWAP 区或者Windows 虚拟 内存 )的限制的,一般服务器管理员配置 JVM 参数时,会根据实际 内存 设置-Xmx 等参数信息,但经常忽略掉直接 内存 ,使得各个 内存 区域总和大于物理 内存 限制(包括物理的和操作系统级的限制),而导致动态扩展时出现OutOfMemoryError 异常

此外,按照jvm 规范,本地直接内存的最大值按以下顺序设定:
(1 )通过-XX:MaxDirectMemorySize=<size> 指定值
(2 )若(1 )未满足,则就取maxMemory ,也就是通过-Xmx 设定的值;
(3 )若(1 )、(2 )都未满足,则取默认值:64M ;

根据以上知识,结合此次测试情况,问题基本水落石出:

在我们测试日常机中,系统启动的时候设定-Xmx 3072m ,没有通过-XX:MaxDirectMemorySize 设定本地直接内存最大值,因此本地直接内存最大值就是-Xmx 设定的值3072m ,整个系统的物理内存为4G ,除掉系统进程占用的内存,剩下的物理内存加上swap 空间也就接近3G 。设想JVM 的 heap size 占用了1.5G ,direct memory 使用了1.5G ,这时候程序申请一100M 的direct 内存,在这种情况下无论是JVM heap size 还是direct memory 不满足触发gc 的条件,于是jvm 向os 申请分配内存,但是OS 却无可分配的内存了,于是就会抛出OutOfMemoryError 错误。

因此,在使用NIO 框架时的时候一定要注意:
如果该NIO 框架使用的直存,需谨慎设定JVM 运行参数,最好用-XX:MaxDirectMemorySize 进行设定,否则你就得清楚你设定的-Xmx 不单单制定了heap size 的最大值,它同时也是direct memory 的最大值;

再大概补充一下NIO 和OOM 知识:

 

一、 首先对于可用内存这一概念的理解

在32 位机器上,CPU 可寻址的物理内存空间最大是4G ,超出4G 将不再可见。【此处忽略PAE 支持,如果进程中使用了AWE(windows) 或者mmap(linux) 一类的方案,这里暂时不管了】

这4G 的物理内存空间又分为用户空间和内核空间。默认情况下,windows 按照50:50 的比例划分,linux 默认下用户空间3G ,内核空间1G 。

所以一个进程可用的物理内存空间,在linux32 位机器下,就是3G 。而在64 位机器下,基本上可以认为是没有任何限制,原理很简单了。。。

不管是linux 还是windows ,可用内存空间由:物理内存+swap/ 虚拟内存组成。Linux 上称作swap 【交换空间】,windows 上称作虚拟内存,本质上都是拿磁盘的一块地方当作物理内存使用。程序是不用关心使用的是物理内存,还是swap ;程序操作的是虚拟地址空间, OS 再将虚拟地址空间映射到物理内存、文件或者其他。不管是操作物理内存,还是swap ,对于程序来说完全是透明的。

Swap/ 虚拟内存啥时候会使用,这个我也没完全搞清楚,不过有一点应该没错的,就是进程新申请的内存,不会在swap/ 虚拟内存中分配,而是直接在物理内存中分配。当内存紧张时,OS 会将活动进程中占用的内存,从物理内存中交换出来,放到Swap/ 虚拟内存上(有时甚至内存不紧张也会这么干)。当进程恢复活动时,OS 再将数据从swap/ 虚拟内存空间中读出来放到物理内存中

所以当需要分析和计算进程需要占用的内存空间时,可以简单地忽略swap/ 虚拟内存的概念 【这一点需要深入再论证一下!】

二、 JVM 对内存的管理

画一张图,很容易就可以理解了,下面这个圆表示jvm 进程所占用的所有的内存空间,分成三部分:

 

1.   堆空间

包括年轻化、年老代、持久域【以SUN HOTSPOT 虚拟机实现为例,其他虚拟机会有区别,比如IBM 的虚拟机,所谓的“ 持久域” 不是在堆分配,而是在本地内存】

如果这个空间不够了,会抛出java.lang.OutOfMemoryError

2.   栈空间

每个线程都会有一个单独的stack 空间,JDK5.0 以前默认好象是256K ,JDK5.0 默认是1M ,很大的一个数值,可以通过-Xss 设置。如果这个空间不够了,会抛出java.lang.StackOverflowError

3.   本地内存

Jvm 进程可使用的内存, 除去堆、栈空间之后,剩下来的就是本地内存

以上三个空间加起来的内存,就是最终jvm 进程所使用的所有内存。如果是在32 位机器下,不能超过用户空间大小,即3G ;在64 位机器下,就要看物理内存的大小了

另再提醒一下大家,在发生了内存不足时,一味地增加-Xms 和-Xmx ,很有可能会适得其反,道理应该很明显了。需要看OOM 的类型,是堆不足,还是栈(StackOverFlow) 不足,还是本地内存不足native memory 。jvm 一般都会有足够的信息提示的。

三、 Nio 的direct memory allocate

我理解的,NIO 的直接内存分配【DMA 】,应该是从本地内存区域中分配内存。像前面讲的,如果不使用-XX:MaxDirectMemorySize 设置,那它就会使用-Xms 的设置,以日常测试环境为例, 这种情况下DMA 需要3G ,堆也需要3G, 很明显实际上这两个空间得到的内存都不可能这么大,所以要么是堆空间被挤压,拿不到3G ,要么是DMA 拿不到足够的空间

看jvm 抛出来的错误,应该是堆空间被挤压导致的。如果是本地内存不足,抛出的应该是OutOfMemoryError :Direct buffer memory ,可以看一下java.nio. DirectByteBuffer 这个类的源码,98 行

四、 NIO2.0 的改进

NIO 的DMA ,性能肯定比在堆中分配要好得多,因为是直接操作本地内存,避免了数据在JVM Heap 和本地内存之间的拷贝操作,尤其是数据量较大时应该更加明显。



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [问题 oom 原创] 推荐:

问题排查之OOM (非原创,来自于同事的邮件分享)

- - 一个只愿意做到80分的懒人
非原创,来自于同事的邮件分享. 前段时间在测试过程中发现了mina 框架的问题:当mina 一次传输的文件超过一定值(如55m )或者连续传输文件的次数过于频繁,就会内存溢出:. 2 )之后,又上网查了些资料,才发现mina 不是用的堆内存(Heap ),而是使用的本机直接内存(Direct Memory ).

菜鸟也能解决android中的OOM问题

- - CSDN博客移动开发推荐文章
只要你记住下面几个原则,在android 中处理图片的OOM问题绝对是easy之极:. 1.超大图片要按比例压缩之后才做显示,退出当前activity 必须回收. 关于inSampleSize 可根据自己的实际情况去定. 2.大图片(30~50k)的可直接显示,退出当前activity 立即回收. 3.大量的小图 或者不同size的图片要展示,请参看我的另外一篇LRU算法缓存图片的:http://blog.csdn.net/androidzhaoxiaogang/article/details/8211649.

Hive中跑MapReduce Job出现OOM问题分析及解决

- - CSDN博客云计算推荐文章
今天在跑一段很复杂而且涉及数据量10多年的N个表join的长SQL时,发生了OOM的异常. 由于一个map通常配置只有64MB或者128MB,则在Map阶段出现OOM的情况很少见. 所以一般发生在reduce阶段. 但是今天这个异常详细的看后,会发现既不是map阶段,也不是reduce阶段,发现不是执行过程,而是driver提交job阶段就OOM了.

redis进程OOM被linux内核kill问题调查 - 简书

- -
运维人员收到zabbix告警说codis集群usa-9节点所在机器,原swap 4G 空间只剩下80k. 其立即登录该机器增加了约6G的swap空间. 接着收到某个应用的500错误告警,错误堆栈里提到codis该usa-9节点 “JedisConnectionException: Unexpected end of stream”,再次登录usa-9拿到 linux的系统日志如下:.

Android之批量加载图片OOM问题解决方案 - 杰瑞教育

- - 博客园_首页
一、OOM问题出现的场景和原因.   一个好的app总少不了精美的图片,所以Android开发中图片的加载总是避免不了的,而在加载图片过程中,如果处理不当则会出现OOM的问题.   首先我们来总结一下,在加载图片过程中 出现的OOM的场景无非就这么几种:. 2、  一次加载的图片过多.   那么为什么在以上场景下会出现OOM问题呢.

Android OOM案例分析

- - 美团点评技术团队
在Android(Java)开发中,基本都会遇到 java.lang.OutOfMemoryError(本文简称OOM),这种错误解决起来相对于一般的Exception或者Error都要难一些,主要是由于错误产生的root cause不是很显而易见. 由于没有办法能够直接拿到用户的内存dump文件,如果错误发生在线上的版本,分析起来就会更加困难.

高并发下的oom killer

- - 操作系统 - ITeye博客
最近在搞分布式批处理平台的项目,在进行压力测试的过程中出现oom killer,而且是在linux'系统日志抛出的;. 环境:VMware虚拟机(8c/16g/100g),并发线程数:16个,称此系统为A,在A系统处理的过程中需要调用B系统的服务,是通过http协议进行的调用;. 下面描述一下排查错误的过程及相关的知识,其中一些文章是转载的一下比较好的文章;.

Android 内存溢出解决方案(OOM)

- - CSDN博客移动开发推荐文章
众所周知,每个Android应用程序在运行时都有一定的内存限制,限制大小一般为16MB或24MB(视平台而定). 因此在开发应用时需要特别关注自身的内存使用量,而一般最耗内存量的资源,一般是图片、音频文件、视频文件等多媒体资源;由于Android系统对音频、视频等资源做了边解析便播放的处理,使用时并不会把整个文件加载到内存中,一般不会出现内存溢出(以下简称OOM)的错误,因此它们的内存消耗问题暂不在本文的讨论范围.

Linux的OOM killer简单测试

- - Linux - 操作系统 - ITeye博客
       顾名思义,OOM(out of memory) killer,是Linux操作系统发现内存不足时,它会强制杀死一些用户进程(非内核进程),来保证系统有足够的物理内存进行分配.     Linux对大部分申请内存的请求都回复"yes",以便能跑更多更大的程序. 因为申请内存后,并不会马上使用内存.

Node.js 内存溢出OOM分析

- -
Node.js 内存飙涨以及 OOM 的问题,只要业务流量稍微复杂,一般都会遇到. 如果是堆内内存,在 OOM 之前可以打一个 Heap Profiling 进行分析,如果是 OOM 之后,可以利用 llnode 对 corefile 进行分析,但如果是堆外内存飙涨呢. 这一块内存通过 Chrome Devtool 工具是分析不出来的.