内存段
内存段在 Flink 内部叫 MemorySegment,是 Flink 中最小的内存分配单元,默认大小32KB。Flink内存是区分堆内和堆外的,但是MemorySegment是最小单元,不区分堆内核堆外的.它即可以是堆上内存(Java的byte数组),也可以是堆外内存(基于Netty的DirectByteBuffer),同时提供了对二进制数据进行读取和写入的方法。
HeapMemorySegment:用来分配堆上内存
HybridMemorySegment:用来分配堆外内存和堆上内存,2017年以后的版本实际上只使用了HybridMemo`rySegment。
他有什么有点? 他为什么要设置这个抽象?
假设我们有一个Tuple3<Integer,Double,Person> 的数据.
如下图展示一个内嵌型的Tuple3<Integer,Double,Person> 对象的序列化过程:
Tuple3<Integer,Double,Person> 对象如果用java的JVM自己来管理就会出现密度不紧凑,散列的等情况.而Flink的内存段MemorySegment 他不是散列的,他是紧密连在一起的.
可以看出这种序列化方式存储密度是相当紧凑的。其中 int 占4字节,double 占8字节,POJO多个一个字节的header,PojoSerializer只负责将header序列化进去,并委托每个字段对应的serializer对字段进行序列化.
内存页
内存页是内存段更高一层的封装.
内存页是MemorySegment之上的数据访问视图,数据读取抽象为DataInputView,数据写入抽象为DataOutputView。使用时就无需关心MemorySegment的细节,会自动处理跨MemorySegment的读取和写入。
有了内存页之后,我们就不需要关注,这一段是第几段了.只需要关注是第几页就ok了.
Buffer
一个Buffer对应一个内存段(MemorySegment)
Buffer是网络缓冲内存.是数据传输的时候会用到的.
Task算子之间在网络层面上传输数据,使用的是Buffer,申请和释放由Flink自行管理,并不是由我们代码指定,实现类为NetworkBuffer。1个NetworkBuffer包装了1个MemorySegment。同时继承了AbstractReferenceCountedByteBuf,是Netty中的抽象类。
1 | public class NetworkBuffer extends AbstractReferenceCountedByteBuf implements Buffer { |
Buffer资源池
Buffer资源池类似我们的连接池,比如说我们连接mysql.创建一次连接是一个连接对象,一个线程.但是我们通常会用一个链接池来管理.Buffer资源池和连接池其实是一样的.
BufferPool 用来管理Buffer,包含Buffer的申请、释放、销毁、可用Buffer通知等,实现类是LocalBufferPool,每个Task拥有自己的LocalBufferPool。
Buffer资源池是如何创建的呢?
他用了一个工厂. 设计模式的工厂模式创建的.
BufferPoolFactory 用来提供 BufferPool 的创建和销毁,唯一的实现类是NetworkBufferPool,每个TaskManager只有一个NetworkBufferPool。同一个TaskManager上的Task共享NetworkBufferPool,在TaskManager启动的时候创建并分配内存。
- 本文作者: xubatian
- 本文链接: http://xubatian.cn/Flink内存管理-内存数据结构/
- 版权声明: 本博客所有文章除特别声明外均为原创,采用 CC BY 4.0 CN协议 许可协议。转载请注明出处:https://www.xubatian.cn/