Spark 1.6 开始使用了统一内存管理模块(UnifiedMemoryManager),并引入了堆外内存(Off-heap memory),1.6之前的内存管理就不进行介绍了。
spark堆内和堆外内存模型的示意图
(注意:堆外内存是依赖于worker的,而不是executor)
spark堆外:(之所以叫spark堆外,是因为后续要和jvm堆外进行区别,也是大家容易弄混淆的地方),为了进一步优化内存的使用以及提高 Shuffle 时排序的效率,Spark 引入了堆外(Off-heap)内存,使之可以直接在工作节点的系统内存中开辟空间。Spark 可以直接操作堆外内存,减少了不必要的内存开销,以及频繁的 GC 扫描和回收,提升了处理性能。堆外内存可以被精确地申请和释放,而且序列化的数据占用的空间可以被精确计算,所以相比堆内内存来说降低了管理的难度,也降低了误差。
在默认情况下堆外内存并不启用,可通过配置 spark.memory.offHeap.enabled 参数启用,并由 spark.memory.offHeap.size 参数设定堆外空间的大小。除了没有 other 空间,堆外内存与堆内内存的划分方式相同,所有运行中的并发任务共享存储内存和执行内存。
堆外内存由于默认不开启堆外内存,以及笔者没有使用堆外内存的客观收益证明,这里不进行详细讨论
spark堆内:spark 堆内内存就是spark提交给jvm管理的那部分内存(与直接操作系统内存对应),spark 对堆内内存的管理是一种逻辑上的"规划式"的管理,因为对象实例占用内存的申请和释放都由 JVM 完成,Spark 只能在申请后和释放前记录这些内存。这也是为什么堆内占用没法被精确计算(非序列化的对象),以及内存使用情况无法精确得知(无法清楚得知jvm的GC情况)的原因。
spark堆内内存模型示意图
如图所示,spark.{executor/driver}.memory参数来设置堆内存, spark.yarn.{executor/driver}.memoryOverhead 来设置堆外内存。
(注意:这里的memoryOverhead所指的堆外内存是jvm控制的堆外内存,与上面说的spark堆外内存不是一回事)
由于executor和driver的内存模型结构相同,这里以executor为例来进行说明
Execution 内存:主要用于存放 Shuffle、Join、Sort、Aggregation 等计算过程中的临时数据
Storage 内存:主要用于存储 spark 的 cache 数据,例如RDD的缓存、unroll数据;
用户内存(User Memory):主要用于存储 RDD 转换操作所需要的数据,例如 RDD 依赖等信息,对于sparkSQL而言还有UDF的内存占用。注意:这部分数据无法spill到磁盘,如果不够就会OOM
预留内存(Reserved Memory):系统预留内存,会用来存储Spark内部对象。
(注意:公司集群中,spark.memory.fraction的默认值是0.3)
jvm堆外内存的模型
其中堆外内存的Execution和Storage作用和堆内内存相同
参数调优指南(Spark内存模型以及调参建议)
(注意:executor的内存溢出绝大多数情况是由于数据倾斜,所以优先要解决数据倾斜的问题,参数的调优只是在非数据倾斜下才有明显收益)
出错点 |
表现 |
建议 |
Executor 堆内存不足 |
在XT的测试日志或者Spark Driver日志中发现Executor的退出码为143:“Exit status: 143”,例如: Diagnostics: Container killed on request. Exit code is 143 Container exited with a non-zero exit code 143 Killed by external signal |
-
如果executor-cores 比较大(>2), 首先减小executor-cores
-
如果executor-cores 比较小,增大spark.executor.memory
|
在XT测试日志或者SparkUI中发现如下日志“Executor heartbeat timed out”,例如: ExecutorLostFailure(executor 923 exited caused by one of the running tasks) Reason: Executor heartbeat timed out after 167123 ms |
Executor 堆外内存不足 |
在XT测测试日志或者Spark UI中出现“Consider boosting spark.yarn.executor.memoryOverhead”,例如 ExecutorLostFailure (executor 29 exited caused by one of the running tasks) Reason: Container killed by YARN for exceeding memory limits. 3.1 GB of 3 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead. |
-
如果executor-cores 比较大(>2), 首先减小executor-cores
-
如果executor-cores 比较小,增大 spark.yarn.executor.memoryOverhead
|
Driver内存不足 |
XT测试日志中频繁出现“WARN Client: Fail to get RpcResponse: Timeout” |
-
怀疑大表被广播,检查一下是否有将spark.sql.autoBroadcastJoinThreshold设置比较大,或者对大表加了广播的hint(mapjoin、broadcast)
-
检查是否向Driver collect大量数据
-
增大spark.driver.memory
|
JobSearch 出现“Application application_1556014017728_7970183 failed 1 times due to ApplicationMaster for attempt appattempt_1556014017728_7970183_000001 timed out. Failing the application.”, 例如 |
Driver压力大,不能及时响应Executor的心跳信息 ExecutorLostFailure (executor 696 exited caused by one of the running tasks) Reason: Container marked as failed: container_e17_1560590242140_874934_01_000898 on host: zw02-data-hdp-dn6412.gh.sankuai.com. Exit status: 56. |