flink内存需求
flink将子任务分布在不同的taskmanager上并行执行,每一个taskmanager中启动一个jvm进程,flink应用本质就是jvm进程,所以必然需要jvm内存的一些固定开销,例如:jvm元空间:存放类的元数据;jvm执行开销:线程的栈,IO以及编译缓存等,另外flink框架本身以及task执行用户代码时也要占用一定的内存,当数据需要进行写入磁盘或者网络传输的时候还需要一块网络缓冲区内存,最后就是flink特别的需要一块托管内存,这部分内存用于缓存中间结果。
flink内存模型
可以看到,flink的内存模型中:
- flink框架使用了堆上内存和堆外内存,不计入slot资源
- taks执行的内存也使用了堆上和堆外内存
- 网路缓冲内存是多个slot共享的,不隔离
一.flink框架内存
- 用途?flink框架本身占用的内存
- 内存大小?通常情况下,不建议对框架堆内存和框架堆外内存进行调整,这部分的默认值是框架测试时不调度任务空跑通过测量得到的一个数据。
二.task内存
- 用途?userCode和libraies
- 内存大小?当使用HeapStateBackend时尤其需要使用Task Heap堆上内存。
- 各slot之间时严格隔离的。
三.网络缓冲内存
- 用途?网络传输
- 内存大小?网络缓冲内存大小由作业拓扑决定
- 内存大小的影响?若内存不足会导致运行失败。
- 各slot之间不隔离。
四.托管内存
- 用途?托管内存有专门的用途,例如批处理的sort/join算子用来存储中间结果,或者流处理使用的是RocksDBStateBackend时就会使用这部分托管内存,如果作业中不涉及到这些功能,建议把托管内存设为0,避免浪费。
- 内存大小的影响?大或小都能跑,不会运行失败,顶多就是小了会导致性能低。
- 各slot之间时严格隔离的。
五. JVM元空间
- 用途?存放JVM加载的类的元数据
- 内存大小的影响?加载的类越多,需要的空间越大,当作业需要加载大量第三方库,建议调大Metaspace。若不足会出现metaspaceOOM。
六. JVM开销
- 用途?线程栈,编译缓存
- 大小?这部分内存的上限不受JVM限制,可作为预留空间。
内存相关参数: -XmX = 框架堆上内存+task堆上内存,达到上限时触发GC,GC后空间仍然不足则触发OOM异常并退出。OutOfMemoryError:Java heap space.
-XX: MaxDirectMemorySize = 框架堆外内存(部分)+task堆外内存(部分)+网络缓冲内存,达到上限时触发GC,GC后空间仍不足则触发OOM异常并退出。OutOfMemoryError:Direct buffer memory.
-XX: MaxMetaspaceSize = JVM元空间内存,达到上限时触发GC,GC后空间仍不足则触发OOM异常并退出。OutOfMemoryError: Metaspace
而剩下的nativeMemory = 框架堆外内存(部分)+task堆外内存(部分)+ managedMemory + JVM Overhead ,这部分内存的上限是不受JVM严格控制的,特别是managedMemory,它的用量上限是flink控制的。
为什么已经在堆外了还是要触发GC后才释放,因为不论是DirectMemory还是Metaspace,它们在堆内都有一个相应buffer对象与其对应,只有堆内这个对应的对象释放后,堆外的才会相应的释放,所以实际上内存释放还是依赖这个GC的。
jvm中java对象模型缺陷
1.有效数据密度低 java对象由三部分构成,对齐填充仅起到占位符的作用
2.垃圾回收机制 海量数据导致空间不足,可能出现秒级甚至分钟级的Full GC,不仅影响效率,其引起的中断可能导致心跳超时被踢出集群
3.OOM问题 当执行Full GC后空间仍然不足,则抛出OOM导致JVM崩溃,影响分布式框架的健壮性和性能
4.缓存未命中问题 CPU将经常访问的数据及其下一步可能访问的附近的数据搬运到缓存中,以便下次访问,但由于java对象在堆中不连续,所以搬运的附近数据往往不是下一步计算需要的,这就是缓存未命中,导致执行效率降低。
flink自主管理内存来解决jvm的几个问题
flink内存管理的两个突出特点:
- 在flink中,使用固定长度的内存块(默认32KB)来管理java对象,即memorySegment,它是flink中内存分配的最小单元。一个java对象序列化为二进制数据流后可能占用1个或多个memorySegment。
- memorySegment也可以存在堆外内存
基于上面的优化,可以改善在大数据环境下jvm内存模型的缺陷:
-
针对有效数据密度低的问题: 因为只存储实际数据的二进制内容,避免了对齐填充等占位符,节省了内存空间。 -
针对Full GC的问题: 一方面数据以二进制的形式存在memorySegment中一直呆在老年代不会被GC回收,而其他的数据对象基本上是由用户代码生成的短生命周期对象,这部分对象可以被Minor GC快速回收,发生Full GC的概率极低;另一方面memorySegment也可以存在堆外内存,不会引发Full GC。 -
针对OOM的问题: 所有的运行时数据结构和算法只能通过内存池申请内存,保证了其使用的内存大小是固定的,不会因为运行时数据结构和算法而发生OOM。而且由于分配的内存段的数量是固定的,因此监控剩余的内存资源是非常简单的,在内存吃紧的情况下,算法(sort/join等)会高效地将一大批内存块写到磁盘,之后再读回来,防止OOM。 -
针对缓存未命中的问题: 二进制数据以定义好的格式存储,可以高效地比较与操作。另外,该二进制形式可以把相关的值,以及hash值,键值和指针等相邻地放进内存中,使得数据结构可以高速缓存更友好,可以从L1/L2/L3缓存获得性能的提升。
但引入堆外内存也有一些缺点:
- 分配生命周期短的对象,比起堆内内存,在堆外内存上分配开销更高。
- 堆外内存出错时排错更为复杂。
- 在flink的测试中,部分操作在堆外内存上会比堆上内存更慢。
|