Tungsten号称Spark有史以来最大的改动,其致力于提升Spark程序对内存和CPU的利用率,使性能达到硬件的极限,主要工作包含以下三个方面
Memory Management and Binary Processing: leveraging application semantics to manage memory explicitly and eliminate the overhead of JVM object model and garbage collection。Cache-aware computation: algorithms and data structures to exploit memory hierarchy。Code generation: using code generation to exploit modern compilers and CPUs。大致的内容如下
Memory Management and Binary Processing: off-heap管理内存,降低对象的开销和消除JVM GC带来的延时。Cache-aware computation: 优化存储,提升CPU L1/ L2/L3缓存命中率。Code generation: 优化Spark SQL的代码生成部分,提升CPU利用率。上述第一点和内存管理相关,是本篇文章关注的重点。
运行在JVM之上的程序,依赖JVM管理内存及回收垃圾,但会存在两个显著问题
Java对象空间开销大 以UTF-8编码的字符串abcd为例,仅存储字符串信息需要4 bytes,然而使用Java的String存储则需要48 bytes,使用jol打印出String的内部占用(参考Java Object Layout(jol) ),如下
OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 4 char[] String.value N/A 16 4 int String.hash N/A 20 4 int String.hash32 N/A Instance size: 24 bytes 使用jol打印出abcd占用空间大小,如下java.lang.String@213214d1d footprint: COUNT AVG SUM DESCRIPTION 1 24 24 [C 1 24 24 java.lang.String 2 48 (total)GC带来的时间开销 full gc会stop the world,增加程序耗时,甚至可能出现假死情况,并且这种情况没有日志输出,给问题排查带来一定难度。
基于以上考虑,Tungsten项目优化内存的使用,使用off-heap方式管理内存,不再依赖JVM,避免了存储的额外开销及GC的影响。
off-heap是指类似C语言的方式,直接向OS申请及释放的内存。JVM提供了sun.misc.Unsafe API实现上述操作(参考Understanding sun.misc.Unsafe),Spark对应的代码如下 上图中Platform类是对sun.misc.Unsafe的简单封装,返回的内存地址和内存大小封装为MemoryBlock。
以BytesToBytesMap为例介绍上面申请的MemoryBlock的使用。 Spark 内存管理之BytesToBytesMap
介绍Tungsten中内存管理相关部分。
参考: Project Tungsten: Bringing Apache Spark Closer to Bare Metal Java Object Layout(jol) Understanding sun.misc.Unsafe