记一次 MapReduce 的内存溢出
背景:
最近使用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容器的堆内存溢出。
异常图如上,仔细缕了一遍异常,发现是ContainerLauncherImpl 这个类的报错,意思是容器启动的时候发生的问题。
查了下hadoop 这块的代码,果然是申请容器的时候出了问题,MRAppMaster负责向yarn的resourcemanager去申请资源启动容器。初步得出了应该是MRAppMaster的内存溢出。
ApplicationMaster向资源调度器申请执行任务的资源容器Container,运行用户自己的程序任务job(我们可以用浏览器看yarn
里的job进展),监控整个任务的执行,跟踪整个任务的状态,处理任务失败以异常情况。这也对应了我们看yarn上的任务监控会崩溃的情况。
解决:
通过调整参数,yarn.app.mapreduce.am.command-opts
、yarn.app.mapreduce.am.resource.mb
加大了MRAppMaster的内存。同时分析待处理的源数据。发现每个文件10MB大小,总共有18万,MapReduce处理文件,默认使用FileInputFormat,但是这个处理小文件是,会每一个小文件生成一个map,所以以为着原来我们的任务有18万个map,这应该突破了原来的MRAppMaster所能维护的map的极限,频繁的申请创建、销毁container也消耗了大量的计算资源,导致了内存溢出、任务崩溃。hadoop处理大量小文件建议使用CombineFileInputFormat来合并输入数据,减少map数量。修改完之后,map数量锐减到2800多个,程序不再报内存溢出,同时提升了执行速度。
至此,问题得到了解决。
看完三件事❤️
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
关注公众号 『 java烂猪皮 』,不定期分享原创知识。
同时可以期待后续文章ing🚀
评论