本文主要简单介绍TaskManager的内存管理策略,以下均为笔者个人观点,欢迎大家批评指正。
一 、内存布局
在flink中,TaskManager内存主要分为三大块
- JVM使用的内存
- 网络内存池
- Flink自己管理的内存
Flink自己管理的内存:这个内存,指的是flink的预留内存,这部分内存可以在jvm堆内,也可以在jvm堆外,若如此(在堆外),这部分内存不会受jvm管辖。照网上说法,这样的好处有:
以下4点优势来自于网络
1、内存安全执行和高效的核外算法
2、减少垃圾收集压力
3、节省空间的数据存储 Java 对象具有存储开销。
4、高效的二进制操作和缓存敏感性 。
与此同时,若这部分内存不在堆外,此时,这部分内存就是jvm堆内存的一部分,如果没有设置
taskmanager.memory.preallocate=true
那么此时flink自己的这部分内存只是一个逻辑上的概念,并不是实际存在。
一般,flink的流处理作业时,是不会设置这个值的。flink批处理时,可能会设置这个值。
个人理解,这部分内存大多用于排序等操作,离线批处理时,数据量比较大,有时需要申请一部分flink自己管理的内存用于排序等操作,注意这个排序等操作完全是flink底层内部operator自己去使用的,外部无法干预。 而流处理作业时,一般是基于时间窗口,所以这部分内存没有必要申请。
如果一个流处理作业申请了这部分内存,如果这部分内存满了,可以直接覆盖写。(流处理情况下一般不建议申请这部分内存,taskmanager.memory.preallocate默认为false)。
有了以上基础,下面将简要概述一下flink中这三块内存的使用
1.1 JVM 使用的内存
JVM使用的内存主要由Heap内存和Non-Heap内存两部分构成,JVM的内存结构如下图所示:
上图其实就是java的内存模型,先放了张网图。
关于jvm的内存模型本文不再赘述,这里需要明确一点,在运行时,Flink只限制了Heap内存的使用,对Non-Heap的内存使用没有限制。(这个Non-Heap指的是jvm中除了堆之外的内存,不是指flink管理的内存) 。
1.2 网络内存池
Flink单独从堆外内存中划分了部分内存作为网络传输内存池, 当算子需要发送数据时,会首先从堆外内存里申请一个缓存页,将数据序列化存储到缓存页里。这部分和flink的反压也颇有渊源。
内存池的基本单位是 memory-segment, 默认大小是32k。
官方建议需要缓存页数量评估值为下图
根据我们配置的taskmanager.memory.segment-size 可以大概估算出需要使用的内存
配置项 | 含义 |
Fraction of JVM memory to use for network buffers (DEFAULT: 0.1) | |
Minimum memory size for network buffers (DEFAULT: 64MB) | |
Maximum memory size for network buffers (DEFAULT: 1GB) | |
taskmanager.memory.segment-size | Size of memory buffers used by the memory manager and the network stack in bytes |
1.3 Flink自己管理的内存
Flink是支持二进制排序的,在批处理模式下,一些操作比如sort,join操作可以将数据序列化存储到单独分配的内存块中,以压缩对JVM堆内存的使用。
需要说明的是:
- 这部分内存是由算子来主动申请使用的,如果没有申请,则不会使用
- 通常不会将taskmanager.memory.preallocate选项配置为true,如果算子不去申请,那么这些预分配的内存永远不会被使用
- TaskManager的slot是均分这块内存池
1.4 内存分布
在提交作业时,我们可以通过-tm指定TaskManager使用的内存,也即整个Container使用的内存
根据Flink自己管理的内存是否使用堆内存,整个TaskManager的内存划分可以分成以下两种情况
flink使用堆内内存的内存分配示意图
使用堆外内存的内存分配示意图
以上内容为笔者对于flink内存管理第一节(内存布局)的简单理解,如有不正确欢迎大家批评指正。关于flink内存模型的其他内容会在下一篇中体现。谢谢。