目前Spark-2.1.0版本还保留了StaticMemoryManager静态内存管理的逻辑,但是在代码里默认还是使用了统一内存管理。两个管理器之间选择的控制参数是:spark.memory.useLegacyMode(设置为true时使用静态内存管理,2.1.0版本默认为false)
UnifiedMemoryManager 的实现思路:
- 逻辑上还是分为两块内存,一块用来存储(Storage),一块用来计算(Compute),在代码里分别维护了一个内存池:StorageMemoryPool和ComputeMemoryPool。
- 执行内存是指shuffle,join,aggregate,sort等计算过程使用的内存,存储内存是指计算过程中缓存或者传输内部数据时使用的内存。
- 执行内存和存储内存共享同一块内存空间。当各自内存池有空闲时,两者是可以彼此借用内存空间的。执行内存在需要时,可以强制存储内存spill数据到磁盘,腾出内存空间给它,直到存储内存下降到设定的阈值之下;但存储内存没有权限强制执行内存将正在使用的内存借给它,官方的解释是因为实现起来比较复杂。道理很容易想明白,对执行过程的侵入比较容易造成task失败。
简单说下上面的逻辑:大家都富裕的时候,是可以彼此借用,甚至全部可用内存都归属一方使用;当大家资源都紧张的时候,执行方可以强制存储方腾出内存给他,但是有个阈值的保障,保证存储不会把内存都贡献出来;而执行方不会被迫将使用的内存贡献出来。可以理解为执行内存有更高的优先级。 - 统一内存管理的两个参数:
spark.memory.fraction:默认0.6。(heapsize-300M)* 0.6为最大可用内存,剩下的主要存储用户数据和spark内部的元数据。
spark.memory.storageFraction:默认值为0.5。当存储内存下降到最大可用内存的一半时,即使执行内存依然需要内存,存储也不再借出。可以理解为存储内存的安全区域。如果该参数设置过小,数据会频繁spill磁盘,影响任务性能