基本类介绍
spark的内存管理相关的类主要是org.apache.spark.memory包下面(java和scala两部分).
spark的内存管理实际上是对jvm内存的管理的一个逻辑规划,包括分配内存、释放内存等。
org.apache.spark包重点介绍了相关实现类。
package org.apache.spark
/**
* This package implements Spark's memory management system. This system consists of two main
* components, a JVM-wide memory manager and a per-task manager:
* 主要是两个组件,一个是管理JVM的内存,一个是管理单个task的内存
*
* - [[org.apache.spark.memory.MemoryManager]] manages Spark's overall memory usage within a JVM.
* This component implements the policies for dividing the available memory across tasks and for
* allocating memory between storage (memory used caching and data transfer) and execution
* (memory used by computations, such as shuffles, joins, sorts, and aggregations).
* MemoryManager 管理整个JVM内存.此组件实现用于在任务之间划分可用内存以及在存储(用于缓存和数据传输的内存)
* 和执行(计算使用的内存,如随机播放、联接、排序和聚合)之间分配内存的策略。
* - [[org.apache.spark.memory.TaskMemoryManager]] manages the memory allocated by individual
* tasks. Tasks interact with TaskMemoryManager and never directly interact with the JVM-wide
* MemoryManager.
* TaskMemoryManager 管理各个任务分配的内存。
* 任务与 TaskMemoryManager 交互,从不直接与 JVM 范围的 MemoryManager 交互。
*
* Internally, each of these components have additional abstractions for memory bookkeeping:
*
* - [[org.apache.spark.memory.MemoryConsumer]]s are clients of the TaskMemoryManager and
* correspond to individual operators and data structures within a task. The TaskMemoryManager
* receives memory allocation requests from MemoryConsumers and issues callbacks to consumers
* in order to trigger spilling when running low on memory.
* MemoryConsumers是TaskMemoryManager的客户端,对应于任务中的各个运算符和数据结构。
* TaskMemoryManager接收来自MemoryConsumers的内存分配请求,并向消费者发出回调,以便在内存不足时触发溢出。
* - [[org.apache.spark.memory.MemoryPool]]s are a bookkeeping abstraction used by the
* MemoryManager to track the division of memory between storage and execution.
* MemoryPools是MemoryManager用来跟踪存储和执行之间内存划分的薄记抽象。
*
* Diagrammatically:
*
* {{{
* +---------------------------+
* +-------------+ | MemoryManager |
* | MemConsumer |----+ | |
* +-------------+ | +-------------------+ | +---------------------+ |
* +--->| TaskMemoryManager |----+ | |OnHeapStorageMemPool | |
* +-------------+ | +-------------------+ | | +---------------------+ |
* | MemConsumer |----+ | | |
* +-------------+ +-------------------+ | | +---------------------+ |
* | TaskMemoryManager |----+ | |OffHeapStorageMemPool| |
* +-------------------+ | | +---------------------+ |
* +---->| |
* * | | +---------------------+ |
* * | | |OnHeapExecMemPool | |
* +-------------+ * | | +---------------------+ |
* | MemConsumer |----+ | | |
* +-------------+ | +-------------------+ | | +---------------------+ |
* +--->| TaskMemoryManager |----+ | |OffHeapExecMemPool | |
* +-------------------+ | +---------------------+ |
* | |
* +---------------------------+
* }}}
*
*
* There is one implementation of [[org.apache.spark.memory.MemoryManager]]:
*
* - [[org.apache.spark.memory.UnifiedMemoryManager]] enforces soft
* boundaries between storage and execution memory, allowing requests for memory in one region
* to be fulfilled by borrowing memory from the other.
* UnifiedMemoryManager在存储和执行内存之间强制执行软边界,允许通过从另一个区域借用内存来满足对一个区域内存的请求。
*/
package object memory
MemConsumer是spark中不同组件和场景使用内存的客户端(TaskMemoryManager的客户端),使用它来操作内存。
TaskMemoryManager是管理task的内存,它是MemoryManager的客户端。
MemoryManager管理jvm内存,分成四个内存池:
- OnHeapStorageMemPool 堆内存储内存池
- OffHeapStorageMemPool 堆外存储内存池
- OnHeapExecMemPool 堆内执行内存池
- OffHeapExecMemPool 堆外执行内存池
MemoryPool
MemoryPool是一个动态调节大小的内存池。
StorageMemoryPool
存储内存池,主要方法有四个。
- memoryUsed 获取已经使用的内存大小
- acquireMemory 申请内存
- releaseMemory 释放内存
- freeSpaceToShrinkPool 收缩内存池(为了给执行内存池腾出更多的内存)
memoryUsed
_memoryUsed变量是用来记录内存使用大小。
acquireMemory
releaseMemory
freeSpaceToShrinkPool
freeSpaceToShrinkPool是在收缩内存池之前调用的,返回要收缩的值。它并不执行实际收缩的动作。
ExecutionMemoryPool
执行内存池,主要属性和方法有四个:
- memoryForTask map结构,记录了task和它对应内存大小的关系
- memoryUsed 使用的总内存大小
- acquireMemory 申请内存
- releaseAllMemoryForTask 释放内存
memoryUsed
使用的总内存大小memoryUsed是每个task使用内存之和。
acquireMemory
- 添加task到memoryForTask中,此时task数量发生改变,通知线程(此处指未能申请到内存的等待线程)可以重新申请内存
- 判断是否需要扩容,需要的话就扩容
- 计算可以申请的内存大小 toGrant
- toGrant大于此次申请的内存 numBytes,同时 curMem + toGrant < minMemoryPerTask,标识此时申请的内存不够,阻塞当前线程。等待内存释放或者task数量变化后唤醒。重复2~4,直到申请到足够的内存。
扩容 maybeGrowPool
releaseMemory
释放内存完成后,唤醒线程,通知可以申请内存。
MemoryManager
用四个MemoryPool来记录和管理内存。
堆外的两个pool大小默认都是0.不参与管理
堆内的两个pool是有onHeapStorageMemory和onHeapExecutionMemory决定,这两个参数是创建MemoryManager时传入的。
从功能上看,主要是管理三类内存(ExecutionMemory、StorageMemory、UnrollMemory)的申请和释放。
UnrollMemory 是指数据读取过程,不知道具体需要多大内存,所以可以随着数据增多,UnrollMemory会不断扩大,最后将数据全部读取到内存。最后UnrollMemory会转变为StorageMemory。
内存申请都是在实现类(UnifiedMemoryManager)中实现的。
内存释放比较简单,对应的内存池释放内存即可。
UnifiedMemoryManager
内存的申请也是操作的内存池来申请内存。
UnrollMemory内存申请就是StorageMemory内存申请。
StorageMemory申请过程中StoragePool空间不够,会向ExecutionPool借用其剩余内存。
ExecutionMemory申请过程中ExecutionPool空间不够,会向StoragePool借用,StoragePool会将缓存的block驱除,腾出空间来给ExecutionPool。
内存模型
内存模型划分在UnifiedMemoryManager的伴生对象中。