6.2 内存模型
Flink 1.10以前的版本中内存模型存在一些缺陷,导致优化资源充分利用率比较困难,例如:
● 流和批处理内存占用的配置模型不同,配置参数多、关系乱。
● 流处理中的RocksDBStateBackend需要依赖用户进行复杂的配置。
为了让内存配置对于用户更加清晰、直观,Flink 1.10版本对TaskExecutor的内存模型和配置逻辑进行了较大的改动(FLIP-49)。这些改动使得Flink能够更好地适配所有部署环境(如Kubernetes、Yarn、Mesos),让用户能够更加严格地控制其内存开销。
6.2.1 内存布局
TaskManager是Flink中执行计算的核心组件,是用来运行用户代码的Java进程。其中大量使用了堆外内存。
Flink TaskManager的简化和详细内存结构如图6-2所示。
图6-2 Flink TaskManager简化和详细内存模型
从大的方面来说,TaskManager进程的内存模型分为JVM本身所使用的内存和Flink使用的内存,Flink使用了堆上内存和堆外内存。
1. Flink使用的内存
(1)JVM堆上内存
1)框架堆上内存Framework Heap Memory。Flink框架本身所使用的内存,即TaskManager本身所占用的堆上内存,不记入Slot的资源中。
配置参数:taskmanager.memory.framework.heap.size = 128MB,默认128MB。
2)Task堆上内存Task Heap Memory。Task执行用户代码时所使用的堆上内存。
配置参数:taskmanager.memory.task.heap.size。
(2)JVM堆外内存
1)框架堆外内存Framework Off-Heap Memory。Flink框架本身所使用的内存,即TaskManager本身所占用的堆外内存,不记入Slot资源。
配置参数:taskmanager.memory.framework.off-heap.size = 128MB,默认128MB。
2)Task堆外内存Task Off-Heap Memory。Task执行用户代码时所使用的堆外内存。
配置参数:taskmanager.memory.task.off-heap.size = 0,默认为0。
3)网络缓冲内存Network Memory。网络数据交换所使用的堆外内存大小,如网络数据交换缓冲区(Network Buffer,后文会介绍)。
配置参数:taskmanager.memory.network.[64/1024/0.1])(min/max/fraction),默认min=64MB, max=1gb, fraction=0.1。
4)堆外托管内存Managed Memory。Flink管理的堆外内存。
配置参数:taskmanager.memory.managed.[size|fraction]),默认fraction=0.4。
2. JVM本身使用的内存
JVM本身直接使用了操作系统的内存。
(1)JVM元空间
JVM元空间所使用的内存。
配置参数:taskmanager.memory.jvm-metaspace =96m,默认96MB。
(2)JVM执行开销
JVM在执行时自身所需要的内容,包括线程堆栈、IO、编译缓存等所使用的内存。
配置参数:taskmanager.memory.jvm-overhead = [min/max/fraction])。默认min=192MB,max=1GB, fraction=0.1。
3. 总体内存
(1)Flink使用内存
综上而言,Flink使用的内存包括Flink使用的堆上、堆外内存。使用参数taskmanager. memory.flink.size进行控制。
(2)进程使用内存
整个进程所使用的内存,包括Flink使用的内存和JVM使用的内存。使用参数taskmanager.memory.process.size进行控制。
JVM内存控制参数如下所示。
1)JVM堆上内存,使用-Xmx和-Xms参数进行控制。
2)JVM直接内存,使用参数-XX:MaxDirectMemorySize进行控制。对于托管内存,使用Unsafe.allocateMemory()申请,不受该参数控制。
3)JVM Metaspace使用-XX:MaxMetaspaceSize进行控制。
6.2.2 内存计算
目前的实现中,在JVM启动之前就需要确定各个内存区块的大小。一旦JVM启动了,在TaskManager进程内部就不再重新计算。Flink中有两个地方进行内存大小计算:
● 在Standalone部署模式下,内存的计算在启动脚本中实现。
● 在容器环境下(Yarn、K8s、Mesos),计算在ResourceManager中进行。
在启动脚本与容器环境下的内存大小计算都调用了Fink的Java代码时间,保证了所有部署模式下的统一,计算好的参数使用-D参数交给Java进程。
计算时,需要配置如下3个参数组合中的至少1个。
(1)Task的堆上内存和托管内存
如果手动配置了网络缓冲区内存大小,则使用该参数。如果没有明确配置,则使用分配系数fraction×总体Flink使用内存计算网络缓冲区内存大小。
(2)总体Flink使用内存
如果配置了该选项,而没有配置(1),则从总体Flink内存中划分网络缓冲区内存和托管内存,剩余的内存作为Task堆上内存。
如果手动设置了网络缓冲内存,则使用其值,否则使用默认的分配系数fraction×总体Flink内存。
如果手动设置了托管内存,则使用其值,否则使用默认的分配系数fraction×总体Flink内存。
(3)总体进程使用内存
如果只配置了总体进程使用内存,则从总体进程中扣除JVM元空间和JVM执行开销内存,剩余的内存作为总体Flink使用内存。