Spark内存溢出- OutOfMemoryError memoryOverhead

Dpark内存溢出

Spark内存溢出
   堆内内存溢出
   堆外内存溢出

堆内内存溢出

java.lang.OutOfMemoryError: GC overhead limit execeeded
java.lang.OutOfMemoryError: Java heap space
 具体说明
  Heap size  JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置.
  JVM在启动的时候会自动设置Heap size的值,
  Heap size 的大小是Young Generation 和Tenured Generaion 之和。
  提示:在JVM中如果98%的时间是用于GC且可用的Heap size 不足2%的时候将抛出此异常信息
  Driver heap OOM的三大原因:
   (1).用户在Driver端口生成大对象, 比如创建了一个大的集合数据结构
(2).从Executor端收集数据回Driver端
 Executor heap OOM

堆外内存溢出

报错情况

Container killed by YARN for exceeding memory limits. 1*.4 GB of 1* GB physical memory used. 
 Consider boosting spark.yarn.executor.memoryOverhead.

基本内容介绍:

1.executor 和 container
  01.Spark中的 executor 进程是跑在 container 中,所以container的最大内存会直接影响到executor的最大可用内存
  02. yarn.nodemanager.pmem-check-enabled 该参数默认是true,也就是会由它来控制监控container的内存使用
  03. yarn.scheduler.maximum-allocation-mb 设置值6114,也就是说单个container申请的最大内存是这么多,
	   执行任务的时候你的executer需要的内存超出这个值,那么就会被杀掉
	    container超过了内存的限制从而被kill掉
   04.executor执行的时候,用的内存可能会超过 executor-memory
        所以会为executor额外预留一部分内存。spark.yarn.executor.memoryOverhead 代表了这部分内存
		即实际的内存
		  val executorMem = args.executorMemory + executorMemoryOverhead
	05.memoryOverhead
    如果没有设置 spark.yarn.executor.memoryOverhead ,则这部分的内存大小为
        math.max((MEMORY_OVERHEAD_FACTOR * executorMemory).toInt, MEMORY_OVERHEAD_MIN))
	   其中 MEMORY_OVERHEAD_FACTOR 默认为0.1, MEMORY_OVERHEAD_MIN 默认为384m executorMemory 为设置的 executor-memory
	    实际 executorMem= X+max(X*0.1,384)
		设置了的话 
		   executorMem=X +spark.yarn.executor.memoryOverhead  其中 X 是值 args.executorMemory
	06. executorMem 需要满足的条件: executorMem< yarn.scheduler.maximum-allocation-mb 	

2.Yarn 中 contaimer 和 Spark中 partition 之间的关系   
   job会被切分成stages,每个stage切分成task,每个task单独调度,可以把executor的jvm进程看做task执行池
   spark.executor.memory  每个executor使用的内存
    一个executor可以并行执行多个task,实际上一个executor是一个进程,task是executor里的一个线程。
    一个task至少要独占executor里的一个虚拟核心vcore, 一个task要占用几个核心,可以由.config("spark.task.cpus", 1)配置,默认是1即一个task占用一个vcore
    同时并行执行的task最大数量 = executor数目 * (每个executor核数 / 每个task占用核心数)
	总核数= executor-cores * num-executor
	 例如: 每个 executor具有3个 cores 理论上每个executor可以处理1-4个task
3.分区与Task的情况
     读取阶段
         01.从内存中创建 RDD:sc.parallelize(...),那么默认的分区数量为该程序所分配的资源的 CPU 数量。
         02.如果是读取hdfs的文件,
             一般来说,partition的数量等于文件的数量。
             如果单个文件的大小大于hdfs的分块大小,partition的数量就等于 “文件大小/分块大小”。
             同时,也可以使用rdd的repartition方法重新划分partition。
     运算阶段
        经过不同的算子计算后,分区数目又会变化
        Task 的数量是由 Partition 决定的
	在Spark中有两类task,一类是shuffleMapTask,一类是resultTask,
	     第一类task的输出是shuffle所需数据,
		 第二类task的输出是result,

可能的原因:

1、数据出现了倾斜等原因导致其中一个 contaimer 内存负载太大 运行失败
2.	Spark的shuffle部分使用了netty框架进行网络传输,但netty会申请堆外内存缓存 Shuffle时,
    每个Reduce都需要获取每个map对应的输出,
    当一个reduce需要获取的一个map数据比较大 超出配置的限制就报了这个错。
	  通过spark.sql.adaptive.shuffle.targetPostShuffleInputSize 可设置每个 Reducer 读取的目标数据量,其单位是字节,默认值为 64 MB。

解决内存overhead的问题的方法是:

1.将"spark.executor.memory" 从8g设置为12g。将内存调大
 2.将"spark.executor.cores"  从8设置为4。   将core的个数调小。
 3.将rdd/dateframe进行重新分区 。           重新分区(repartition)
 4.将"spark.yarn.executor.memoryOverhead"设置为最大值,可以考虑一下4096。这个数值一般都是2的次幂。

具体参数配置

set spark.sql.adaptive.repartition.enabled=true;
set spark.sql.shuffle.partitions=2000;
set spark.sql.adaptive.shuffle.targetPostShuffleInputSize=67108864;

数据倾斜

数据倾斜解决
1. 热点数据
  01.热点数据已知
    过滤: 过滤无关数据以及影响可接受范围内的热点数据: NULL值以及空字符串等值
    分离:
  02.热点数据未知:
     打散: 添加随机字段前缀等方式 随机key实现双重聚合
 2.执行方式
    数据源数据文件不均匀
    Shuffle过程中数据分布不均
       缓解 - 处理shuffle的情况:
         join中有数据倾斜: 
              一大一小:    将reduce join转换为map join
              两个数据都比较大: 随机数以及扩容表进行join

专有名词解释

1.常用配置
   配置任务可用executor数量
   每个Executor占用内存
   每个Executor的core数目  spark.executor.cores
  
  The maximum memory size of container to running driver 
    is determined  by 
  the sum of 
      spark.driver.memoryOverhead 
      spark.driver.memory.

  The maximum memory size of container to running executor
   is determined by 
  the sum of 
      spark.executor.memory, 
      spark.executor.memoryOverhead, 
      spark.memory.offHeap.size 
	  spark.executor.pyspark.memory.
 Shuffle Behavior
 Memory Management
    spark.memory.fraction
	 在Spark中,执行和存储共享一个统一的区域M
	   代表整体JVM堆内存中M的百分比(默认0.6)。
	    剩余的空间(40%)是为用户数据结构、Spark内部metadata预留的,并在稀疏使用和异常大记录的情况下避免OOM错误
	spark.memory.storageFraction

Note: Non-heap memory includes off-heap memory (when spark.memory.offHeap.enabled=true)
   and memory used by other driver processes (e.g. python process that goes with a PySpark driver) 
   and memory used by other non-driver processes running in the same container

spark.executor.memoryOverhead
    This is memory that accounts for things like VM overheads, interned strings, other native overheads, etc.

spark.memory.offHeap.size
spark.memory.offHeap.enabled

源码

package org.apache.spark.deploy.yarn
    DRIVER_MEMORY_OVERHEAD
	EXECUTOR_MEMORY : Amount of memory to use per executor process
    EXECUTOR_MEMORY_OVERHEAD: The amount of off-heap memory to be allocated per executor in cluster mode
	EXECUTOR_CORES = ConfigBuilder("spark.executor.cores")
	EXECUTOR_MEMORY_OVERHEAD = ConfigBuilder("spark.yarn.executor.memoryOverhead")
     // Executor memory in MB.
      protected val executorMemory = sparkConf.get(EXECUTOR_MEMORY).toInt
      // Additional memory overhead.
      protected val memoryOverhead: Int = sparkConf.get(EXECUTOR_MEMORY_OVERHEAD).getOrElse(
        math.max((MEMORY_OVERHEAD_FACTOR * executorMemory).toInt, MEMORY_OVERHEAD_MIN)).toInt

	// Resource capability requested for each executors
     private[yarn] val resource = Resource.newInstance(executorMemory + memoryOverhead, executorCores)

package org.apache.spark.memory;
    public enum MemoryMode { ON_HEAP, OFF_HEAP}
	private[spark] abstract class MemoryManager(
      conf: SparkConf,
      numCores: Int,
      onHeapStorageMemory: Long,
      onHeapExecutionMemory: Long) extends Logging {
     # Tracks whether Tungsten memory will be allocated on the JVM heap or off-heap using sun.misc.Unsafe.
       final val tungstenMemoryMode: MemoryMode = {
         if (conf.getBoolean("spark.memory.offHeap.enabled", false)) {
           require(conf.getSizeAsBytes("spark.memory.offHeap.size", 0) > 0,
             "spark.memory.offHeap.size must be > 0 when spark.memory.offHeap.enabled == true")
           require(Platform.unaligned(),
             "No support for unaligned Unsafe. Set spark.memory.offHeap.enabled to false.")
           MemoryMode.OFF_HEAP
         } else {
           MemoryMode.ON_HEAP
         }
       }

参考:

https://spark.apache.org/docs/latest/configuration.html
  https://spark.apache.org/docs/latest/running-on-yarn.html#configuration