问题排查之OOM 之 mina java.lang.OutOfMemoryError(转)

转载 2013年12月05日 12:09:42

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

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

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

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

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

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

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

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

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

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

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

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

atcom.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。设想JVMheap size占用了1.5Gdirect memory使用了1.5G,这时候程序申请一100Mdirect内存,在这种情况下无论是JVM heap size还是direct memory不满足触发gc的条件,于是jvmos申请分配内存,但是OS却无可分配的内存了,于是就会抛出OutOfMemoryError错误。

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

再大概补充一下NIOOOM知识:

 

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

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以前默认好象是256KJDK5.0默认是1M,很大的一个数值,可以通过-Xss设置。如果这个空间不够了,会抛出java.lang.StackOverflowError

3.  本地内存

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

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

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

三、Niodirect memory allocate

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

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

四、NIO2.0的改进

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

使用mina框架内存溢出的问题

使用mina框架,每当socket连接增多,就会造成内存和cpu使用大幅度地增长,整个服务速度变慢,http请求也变慢了。而且内存下降速度极慢。重启tomcat后又恢复正常。...
  • u013694979
  • u013694979
  • 2017年06月28日 13:52
  • 503

Netty报Direct buffer memory错误

最近在基于Netty实现NIO方面做应用底层通讯架构(服务发现,分布式,高可用,软负载)。遇到java.lang.OutOfMemoryError: Direct buffer memory内存溢出错...
  • u010906369
  • u010906369
  • 2016年12月02日 10:54
  • 2015

发生java.lang.OutOfMemoryError: Direct buffer memory

下面是在掉坑了,出现了一次 java.lang.OutOfMemoryError: Direct buffer memory 错误后的总结。发生原因:用来 nio ,但是 direct buff...
  • github_32521685
  • github_32521685
  • 2015年12月04日 17:07
  • 1271

记一次hive的内存溢出(OutOfMemoryError: Java heap space)排查

刚刚从java组转岗找数据组,学习大数据的知识,开发语言也从java转到python新奇之外也遇到了诸多问题,其中最令我头疼的就是在hive上的统计任务总是三天两头地报告OutOfMemory一开始因...
  • gklifg
  • gklifg
  • 2015年12月28日 14:41
  • 5683

深入理解JVM02--Java 内存溢出(java.lang.OutOfMemoryError)的常见情况和处理方式总结

1、Java堆溢出     Java堆用于存储对象实例,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量限制后就会产生内存...
  • oChangWen
  • oChangWen
  • 2016年05月14日 13:09
  • 1518

从DirectMemory谈谈Java NIO

本机内存DirectMemory,属于C Heap,可以通过参数-XX:MaxDirectMemorySize指定。 如果不指定,该参数的默认值为Xmx的值减去1个Survior区的值。如设置启动参...
  • donsonzhang
  • donsonzhang
  • 2015年06月28日 00:20
  • 2570

使用mina框架内存溢出的问题

使用mina框架,每当socket连接增多,就会造成内存和cpu使用大幅度地增长,整个服务速度变慢,http请求也变慢了。而且内存下降速度极慢。重启tomcat后又恢复正常。...
  • u013694979
  • u013694979
  • 2017年06月28日 13:52
  • 503

NIO通讯框架之阿堂教程:Mina学习笔记-入门篇(一)

在两三年前,阿堂在技术博客(http://blog.sina.com.cn/heyitang) 上曾经写过"JAVA新I/O学习系列笔记(1)"和"JAVA新I/O学习系列笔记(2)"两篇学习笔记...
  • younger_z
  • younger_z
  • 2015年09月08日 11:29
  • 1348

NIO框架之MINA源码解析(二):mina核心引擎

MINA的底层还是利用了jdk提供了nio功能,mina只是对nio进行封装,包括MINA用的线程池都是jdk直接提供的。MINA的server端主要有accept、processor、session...
  • MINEZHANGHAO
  • MINEZHANGHAO
  • 2014年09月03日 08:42
  • 5242

Direct ByteBuffer可能会导致内存泄露的原因

Direct ByteBuffer可能会导致内存泄露的原因
  • LoveTea99
  • LoveTea99
  • 2016年09月17日 15:53
  • 727
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:问题排查之OOM 之 mina java.lang.OutOfMemoryError(转)
举报原因:
原因补充:

(最多只允许输入30个字)