回顾
上一篇文章我们谈了Flink自主内存管理的一些基础的数据结构。那篇中主要讲了数据结构的定义,这篇我们来看看那些数据结构的使用,以及内存的管理设计。
概述
这篇文章我们主要探讨Flink的内存管理类MemoryManager
涉及到对内存的分配、回收,以及针对预分配内存而提供的memory segment pool。还有支持跨越多个memory segment数据访问的page view。
本文探讨的类主要位于pageckage : org.apache.flink.runtime.memory下。完整类图:
MemoryManager
MemoryManager
作为Flink的内存管理器,承担着MemorySegment
的分配、回收等职责。
为了提升MemorySegment
的复用能力,它提供了不同memory type的MemorySegment池的实现,它们是:
- HeapMemoryPool
- HybirdOffHeapMemoryPool
首先,这里为了提升memory segment操作效率,MemoryManager
鼓励长度相等的memory segment。由此引入了page的概念。其实page跟memory segment没有本质上的区别,只不过是为了体现memory segment被分配为均等大小的内存空间而引入的。可以将这个类比于操作系统的页式内存分配,page这里看着同等大小的block即可。MemoryManager
提供的默认page size为32KB,并提供了自定义page size的下界值不得小于4KB。
/** The default memory page size. Currently set to 32 KiBytes. */
public static final int DEFAULT_PAGE_SIZE = 32 * 1024;
/** The minimal memory page size. Currently set to 4 KiBytes. */
public static final int MIN_PAGE_SIZE = 4 * 1024;
MemoryManager
允许自定义page size,它提供的构造器可以指定这个参数,一个MemoryManager
应对一个page size,而且指定了就不允许改变。
page化的segment跟非page化的segment:
将segment page化会给后面的跨多个segment的访问带来更高的效率。
MemoryManager
这个类本身没有特别的地方,并可能会被跨线程共享。这时某些操作可能会牵扯到多线程的并发问题。因此,MemoryManager
提供了一个对象作为锁,以在某些方法上进行同步操作。
/** The lock used on the shared structures. */
private final Object lock = new Object();
两个构造器:
public MemoryManager(long memorySize, int numberOfSlots) {
public MemoryManager(long memorySize, int numberOfSlots, int pageSize, MemoryType memoryType, boolean preAllocateMemory) {
第一个构造器内部调用了第二个构造器,并用DEFAULT_PAGE_SIZE
作为pageSize,HEAP
作为memory type。
需要提及一下的是,这里的参数numberOfSlots
是跟Flink的task manager相关,暂时不做过多介绍,等到我们讲解Flink runtime时再细说。
第二个构造器的另一个参数preAllocateMemory
,指定memory manager的内存分配策略是预分配还是按需分配。我们后面会看到,对于这两种策略,相关的内存申请和释放操作是不同的。
第二个构造器内就已经根据memory type将特定的memory pool对象初始化好了:
switch (memoryType) {
case HEAP:
this.memoryPool = new HeapMemoryPool(memToAllocate, pageSize);
break;
case OFF_HEAP:
this.memoryPool = new HybridOffHeapMemoryPool(memToAllocate, pageSize);