YARN环境下Spark作业调优求助:Java堆内存溢出问题
解决Spark作业Java heap space内存溢出问题
老哥,你已经调了一堆资源参数但还是遇到OOM,咱们得从资源实际分配和作业逻辑优化两方面双管齐下,不能光堆参数,先一步步来:
1. 先确认Yarn实际给你分配了多少资源
很多时候咱们设的参数是“请求值”,但Yarn队列的资源上限可能根本满足不了,导致实际启动的executor数量、内存都打了折扣,这是OOM的隐形原因:
- 作业运行时,用命令
yarn application -status <你的作业AppId>查看实际的Number of Running Containers(就是executor数量)和每个container的内存,确认是不是真的拿到了30个4核10G的executor。 - 如果实际executor数量远小于30,说明
team_high队列的资源不够,要么找运维扩容队列,要么调整参数:比如减少executor数量,同时增加单个executor的内存(比如改成15个executor,每个18G内存),保证总内存资源不变的情况下,让每个executor能处理更多数据。
2. 补全Executor的内存配置细节
你提到尝试过调spark.yarn.executor.memoryOverhead,但当前配置里没体现这个参数,这很关键:
- Spark的executor内存分为堆内存(executor-memory)和非堆内存(memoryOverhead),默认Overhead是堆内存的10%,但如果作业有大量序列化操作、JNI调用或者依赖第三方大库,这个值不够会挤占堆内存,最终触发heap space OOM。
- 建议补充配置:
--conf spark.yarn.executor.memoryOverhead=4G(对应你10G的堆内存,设2-4G比较稳妥);同时driver端也要补:--conf spark.yarn.driver.memoryOverhead=2G。 - 另外,堆内存内部的分配也可以优化:如果你的作业是shuffle密集型,执行内存不够会频繁溢写磁盘,可调整
--conf spark.memory.fraction=0.8(默认0.6,把更多堆内存分给执行和存储)。
3. 从作业逻辑入手(这才是治本之道)
光调资源是治标,作业本身的低效逻辑才是OOM的根源:
- 检查数据倾斜:这是最常见的OOM原因!比如某个key的数据集是其他key的几百倍,导致单个executor要处理巨量数据。可以查看Spark UI的Stage页面,看哪个Task的输入数据量异常大,然后针对性处理:
- 给倾斜的key加随机前缀拆分,分成多个小task处理;
- 过滤掉异常大的无效key;
- 小表join时用
broadcast()广播,避免shuffle产生大量中间数据。
- 优化序列化:默认Java序列化效率低、占内存,换成Kryo序列化能大幅减少内存占用:
--conf spark.serializer=org.apache.spark.serializer.KryoSerializer,如果有自定义类,还可以注册进去提升效率。 - 清理不必要的缓存:如果代码里用了
cache()/persist()但后续没用到,立刻删掉;如果需要缓存,用更省内存的存储级别,比如persist(StorageLevel.MEMORY_AND_DISK_SER)(序列化后存内存+磁盘)。 - 避免在Driver端处理大数据:别用
collect()把全量数据拉到Driver,尽量用foreach()/mapPartitions()在Executor端处理数据,Driver只做结果汇总。
4. 调整参数的小技巧
- 不要盲目堆executor数量:executor-cores设4是合理的(一般不超过5,避免线程上下文切换开销),但要结合队列总资源,比如队列最多有100核,那最多能启动25个4核executor,再多也分配不到。
- 如果作业是计算密集型,适当减少executor内存,增加executor数量;如果是数据密集型,增加单个executor内存,减少数量。
内容的提问来源于stack exchange,提问作者pushpavanthar
相关产品推荐
相关产品推荐

