性能文章>记一次MapReduce的内存溢出>

记一次MapReduce的内存溢出原创

4年前
7598113

背景:

最近使用MapReduce做离线数据清洗,在map段做简单的数据过滤,有经纬度的发送到reduce端,没经纬的过滤掉。reduce端将数据整理出来,按业务模型拼接成字符串写入HDFS。供hive作为外表进行后续数据处理分析。

问题:

该批数据总共2T大小,MapReduce执行第一次时,不出意料的崩溃了。每次都大概在map阶段执行到61%左右。

排查:

查看日志发现果然内存溢出:java.lang.OutOfMemoryError: GC overhead limit exceeded

Exception in thread thread_name: java.lang.OutOfMemoryError: GC Overhead limit exceededCause: The detail message “GC overhead limit exceeded” indicates that the garbage collector is running all the time and Java program is making very slow progress. After a garbage collection, if the Java process is spending more than approximately 98% of its time doing garbage collection and if it is recovering less than 2% of the heap and has been doing so far the last 5 (compile time constant) consecutive garbage collections, then a java.lang.OutOfMemoryError is thrown. This exception is typically thrown because the amount of live data barely fits into the Java heap having little free space for new allocations.Action: Increase the heap size. The java.lang.OutOfMemoryError exception for GC Overhead limit exceeded can be turned off with the command line flag -XX:-UseGCOverheadLimit.

大概意思就是说,JVM花费了98%的时间进行垃圾回收,而只得到2%可用的内存,频繁的进行内存回收(最起码已经进行了5次连续的垃圾回收),JVM就会曝出java.lang.OutOfMemoryError: GC overhead limit exceeded错误。本质上还是堆内存不足。

其实对于大数据执行来说内存溢出问题司空见惯。按照惯例一套标准流程,减小每个map的split字节大小,增加map数,增加每个container的堆内存,增加每个map的堆内存,能想到的参数我试了个遍。其实缕了一下代码,我认为我们的map不应该存在内存溢出的情况。因为并没有进行复杂的关联计算,并且不会存在数据倾斜问题。但是任务每次还是在60%左右卡死,同时,我观察到一个现象。每次程序崩溃的时候,yarn 的任务追踪页面都会反应的异常慢,看来我之前想的方向有问题,看来不是map容器的堆内存溢出。

image.png

异常图如上,仔细缕了一遍异常,发现是ContainerLauncherImpl 这个类的报错,意思是容器启动的时候发生的问题。

image.png

查了下hadoop 这块的代码,果然是申请容器的时候出了问题,MRAppMaster负责向yarn的resourcemanager去申请资源启动容器。初步得出了应该是MRAppMaster的内存溢出。
ApplicationMaster向资源调度器申请执行任务的资源容器Container,运行用户自己的程序任务job(我们可以用浏览器看yarn
里的job进展),监控整个任务的执行,跟踪整个任务的状态,处理任务失败以异常情况。这也对应了我们看yarn上的任务监控会崩溃的情况。

解决:

通过调整参数,yarn.app.mapreduce.am.command-optsyarn.app.mapreduce.am.resource.mb加大了MRAppMaster的内存。同时分析待处理的源数据。发现每个文件10MB大小,总共有18万,MapReduce处理文件,默认使用FileInputFormat,但是这个处理小文件是,会每一个小文件生成一个map,所以以为着原来我们的任务有18万个map,这应该突破了原来的MRAppMaster所能维护的map的极限,频繁的申请创建、销毁container也消耗了大量的计算资源,导致了内存溢出、任务崩溃。hadoop处理大量小文件建议使用CombineFileInputFormat来合并输入数据,减少map数量。修改完之后,map数量锐减到2800多个,程序不再报内存溢出,同时提升了执行速度。

至此,问题得到了解决。

点赞收藏
technology
请先登录,查看1条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

随机一门技术分享之Netty

随机一门技术分享之Netty

从 Linux 内核角度探秘 JDK MappedByteBuffer

从 Linux 内核角度探秘 JDK MappedByteBuffer

MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

13
1