java.lang.OutOfMemoryError:Map failed总结

常见的OOM是以下这几种:
1.GC overhead limit exceeded
2.Java Heap Space
3.Unable to create new native thread
4.PermGen Space
5.Direct buffer memory
6.request {} bytes for {}. Out of swap space?
一直自认为不会有超过这个范围的OOM类型出现,没想到最近看到了一个新的OOM的类型,而这次OOM引发了一次严重的故障,整个排查过程是内部一个同事排查的,文章也是他写的,感谢他的文章,让我也学习到了之前遗漏的一个OOM相关的知识点。

故障现象为
应用日志中发现了大量的OOM异常:
Caused by: java.lang.OutOfMemoryError: Map failed

跟踪堆栈找到抛出异常的地方是在 FileChannle#map,这个方法是创建一个内存映射文件,应用为了降低堆内存的使用,同时提高写入的效率,将一个文件分成多段,内存映射多个MappedByteBuffer进行读写操作;

跟踪fileChannle.map的方法发现最终调用的是FileChannelImpl.c里的方法;

继续跟踪这段代码,发现里面调用的mmap64这个系统函数,当mmap64返回的错误码是ENOMEM时,会向上抛出OOME,进一步查阅了GNU的手册,可以发现抛出ENOMEM错误码的解释:
1. 内存不足;
2. 地址空间不足。

而从当时的现场信息来看,这两点都不成立,当时没有新的思路,于是就先按照FileChannleImpl.unmap方法中的主动释放占用内存的方法改了下代码,改了后应用就一切正常了。

当天这个机器的JVM还crash了一次,crash日志中heap占用和物理内存都是非常正常,但日志中有个现象比较诡异: Dynamic libraries:这部分信息非常多,统计以后发现有65532条。

翻阅资料,发现这个数据来自 /proc/{pid}/maps, 这个文件展示了进程的虚拟地址空间的使用情况,这时突然想到ENOMEM中有说到进程的地址空间不足导致的,但是最后的7fff005aa000还远不到上限,而且计算虚拟内存占用也就几个G的空间。
这时想到前面提到65532这个数据,联想到了file-max,但是一查看是4889494,顺势猜想虚拟内存映射是不是也有打开上限? 不出所料果然是有限制的。

max_map_count这个参数就是允许一个进程在VMAs(虚拟内存区域)拥有最大数量,VMA是一个连续的虚拟地址空间,当进程创建一个内存映像文件时VMA的地址空间就会增加,当达到max_map_count了就是返回out of memory errors。
这个数据通过下面的命令可以查看:
cat /proc/sys/vm/max_map_count

发现应用所在的机器这个数值果然是65536,而且测试修改max_map_count后filechannel#map的个数的上限也随之变化。 所以可以确定程序OOM是由于达到了这个系统的上限,也就是ENOMEM错误码中所指的out of process address。

确定了异常的触发原因,再排查引发的原因就比较容易了,再看下FileChannleImp#map的代码,发现在map第一次出现OOM时,会显式的调用System.gc去回收,但不幸的是应用启动参数上是有-XX:+DisableExplicitGC的,所以就导致了map失败,但如果在代码里主动clean是ok的现象。

总结来说,这个异常出现的原因是:
数据量增长,导致map file个数增长,应用启动参数上有-XX:+DisableExplicitGC,导致了在map file个数到达了max_map_count后直接OOM了(这也是因为heap比较大,所以full gc触发的频率低,这个问题就特别容易暴露)。

从这个问题来看,启动参数上加-XX:+DisableExplicitGC确实还是要小心,不仅map file这里是在OOM后靠显式的去执行System.gc来回收,Direct ByteBuffer其实也是这样,而这两个场景都有可能因为java本身full gc执行不频繁,导致达到了限制条件(例如map file个数达到max_map_count,而Direct ByteBuffer达到MaxDirectMemorySize),所以在CMS GC的场景下,看来还是去掉这个参数,改为加上-XX:+ExplicitGCInvokesConcurrent这个参数更稳妥一点。

时间: 2024-10-17 14:19:23

java.lang.OutOfMemoryError:Map failed总结的相关文章

HTTP Status 500 - Handler processing failed; nested exception is java.lang.OutOfMemoryError: PermGen

HTTP Status 500 - Handler processing failed; nested exception is java.lang.OutOfMemoryError: PermGen space 发生此问题的环境信息: 系统:CentOS 6.5 Tomcat:Tomcat 7.0.6 PermGen space PermGen space的全称是Permanent Generation space,是指内存的永久保存区域OutOfMemoryError: PermGen sp

hive sql执行的job在map时报java.lang.OutOfMemoryError的错误

较为详细且重要的一段报错信息是org.apache.hadoop.mapred.YarnChild: Error running child : java.lang.OutOfMemoryError: Java heap space 说一下解决方案,其实这个解决过程完全是摸索着来的. set mapreduce.map.java.opts=-Xmx1024mset mapred.max.split.size=100000000;set mapred.min.split.size.per.node

java.lang.OutOfMemoryError: Failed to allocate a 3110419 byte allocation with 741152 free bytes and

在进行SurfaceView的开发时,出现了java.lang.OutOfMemoryError错误: 由于项目是同时显示四路远端传输过来的视频 所以采用的方法是使用:android:hardwareAccelerated="false"android:largeHeap="true" 即关闭硬加速,因为他比较消耗内存,另外开启最大内存. 但并不是所有项目都应该使用这样的方法去解决OutOfMemoryError问题,如果是大图片和视频应用可以使用android:l

java.lang.OutOfMemoryError: Java heap space Hadoop

最近,随着大数据的兴起,Java实现的Hadoop成了这个数据领域的领跑者,不管是HDFS,还是MapReduce,还是Hive都成了很热的词汇.也从Hadoop这个软件本体催生了一个依存此的大数据领域,也和时下热的不能再热的云计算拉上了关系. 于是,作为一个程序员,不得不学习新的技术和知识,来保证自己的饭碗,这真是一个极为辛苦的差事.于是,开始接触Hadoop.结果也就是难免不出现错误. <Hadoop Beginner's Guide>这本的指引,进行搭建环境,发现Hadoop已经提供de

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

最近老是遇见服务器内存溢出的问题,故在网上搜了搜,总结了一些java内存溢出的解决方式 java.lang.OutOfMemoryError这个错误我相信大部分开发人员都有遇到过,产生该错误的原因大都出于以下原因:JVM内存过小.程序不严密,产生了过多的垃圾. 导致OutOfMemoryError异常的常见原因有以下几种: 内存中加载的数据量过于庞大,如一次从数据库取出过多数据: 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收: 代码中存在死循环或循环产生过多重复的对象实体: 使用的

解决sqoop报错:java.lang.OutOfMemoryError: Java heap space

报错栈: 2017-06-15 16:24:50,449 INFO [main] org.apache.sqoop.mapreduce.db.DBRecordReader: Executing query: select "CTJX60","CTJX61","CTJX62","CTJX63","CTJX64","CTJX65","CTJX66","CTJX6

java.lang.OutOfMemoryError处理错误

java.lang.OutOfMemoryError异常解决方法 原因: 常见的有以下几种: 1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据: 2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收: 3.代码中存在死循环或循环产生过多重复的对象实体: 4.使用的第三方软件中的BUG: 5.启动参数内存值设定的过小: 常见错误提示: 1.tomcat:java.lang.OutOfMemoryError: PermGen space 2.tomcat:java.lang.Ou

Java.lang.OutOfMemoryError是什么

Java.lang.OutOfMemory是java.lang.VirtualMachineError的一个子类,当Java虚拟机中断,或是超出可用资源时抛出.很明显,OutOfMemory是在Java虚拟机资源耗尽的情况下无法分配对象时抛出的.不过很不幸,Java的说明文档并没有对该异常进行进一步的阐述. Java虚拟机包括六个不同的运行时数据区域(内存区域): 1. 程序计数器(Program Counter Register) 2. Java虚拟机栈(Java VM Stack) 3. J

排查sqoop报错:Error running child : java.lang.OutOfMemoryError: Java heap space

报错栈: 2017-06-16 19:50:51,002 INFO [main] org.apache.hadoop.mapred.MapTask: Processing split: 1=1 AND 1=1 2017-06-16 19:50:51,043 INFO [main] org.apache.sqoop.mapreduce.db.DBRecordReader: Working on split: 1=1 AND 1=1 2017-06-16 19:50:51,095 INFO [mai