首页 理论教育 深入理解Executor的内存模型

深入理解Executor的内存模型

时间:2023-06-29 理论教育 版权反馈
【摘要】:图6-4 Executor JVM内存模型Spark实际上是运行于多JVM之上的分布式计算框架,各个JVM通过RPC框架通信。Spark Execu-tor中默认JVM堆大小是1 GB。例如通过spark.executor. memory配置JVM内存大小为1024 MB,那么安全的阀值就是10240.75 MB。该值默认占Executor And Storage堆空间的50%,可以通过spark.memory.storageFraction配置该比例。Executor内存的大小,和性能本身当然并没有直接的关系,但是几乎所有运行时性能相关的内容都或多或少间接和内存大小相关。

深入理解Executor的内存模型

978-7-111-55442-4-Chapter06-56.jpg

图6-4 Executor JVM内存模型

Spark实际上是运行于多JVM之上的分布式计算框架,各个JVM通过RPC框架通信。既然是JVM,那JVM的堆栈空间大小就可以通过参数配置。Spark Execu-tor中默认JVM堆大小是1 GB。在每一个JVM中,需要分配空间用于缓存数据,还需要分配一定空间用于计算,对于JVM的运行还需要预留部分空间。那么Spark中怎样管理JVM中内存分配的呢?

实际上Spark在JVM内存管理上建有一套模型。该模型图6-4所示:

从图中可以看到,Spark启动的Executor默认的堆大小是1 GB,当然这个默认堆大小可以通过spark.executor. memory来配置。

为了安全考虑同时避免OOM,Spark JVM中允许使用的内存空间占JVM堆空间的75%,该比例可以通过spark.memory.fraction来配置。例如通过spark.executor. memory配置JVM内存大小为1024 MB,那么安全的阀值就是1024∗0.75 MB。

Spark作为一个内存计算框架,可以在内存中缓存数据。Spark通过LRU算法将数据缓存到内存中,这样Spark JVM中就有大量的内存被用来缓存数据。在Spark中用于缓存数据的内存大小是有限制的。该值默认占Executor And Storage堆空间的50%,可以通过spark.memory.storageFraction配置该比例。按照默认值为50%,则用于缓存数据的空间大小为1024∗0.75∗0.5 MB。如果该值设置得过大,那么计算内存占的比例就会变少,这将可能使任务更加频繁的溢出到磁盘。

Executor内存的大小,和性能本身当然并没有直接的关系,但是几乎所有运行时性能相关的内容都或多或少间接和内存大小相关。这个参数最终会被设置到Executor的JVM的heap尺寸上,对应的就是Xmx和Xms的值。(www.xing528.com)

理论上Executor内存当然是多多益善,但是实际受机器配置,以及运行环境,资源共享,JVM GC效率等因素的影响,还是有可能需要为它设置一个合理的大小。多大算合理,要看实际情况。

Executor的内存基本上是Executor内部所有任务共享的,而每个Executor上可以支持的任务的数量取决于Executor所管理的CPU Core资源的多少,因此你需要了解每个任务的数据规模的大小,从而推算出每个Executor大致需要多少内存即可满足基本的需求。

如何知道每个任务所需内存的大小呢?很难统一的衡量,因为除了数据集本身的开销,还包括算法所需各种临时内存空间的使用,而根据具体的代码算法等不同,临时内存空间的开销也不同。但是数据集本身的大小,对最终所需内存的大小还是有一定的参考意义的。

通常来说每个分区的数据集在内存中的大小,可能是其在磁盘上源数据大小的若干倍(不考虑源数据压缩,Java对象相对于原始数据也还要算上用于管理数据的数据结构的额外开销),需要准确的知道大小的话,可以将RDD cache在内存中,从BlockManager的Log输出可以看到每个Cache分区的大小(其实也是估算出来的,并不完全准确)反过来说,如果你的Executor的数量和内存大小受机器物理配置影响相对固定,那么你就需要合理规划每个分区任务的数据规模,例如采用更多的分区,用增加任务数量(进而需要更多的批次来运算所有的任务)的方式来减小每个任务所需处理的数据大小。

如前面所说spark.executor.memory决定了每个Executor可用内存的大小,而spark.memo-ry.storageFraction则决定了在这部分内存中有多少可以用于Memory Store管理RDD Cache数据,剩下的内存用来保证任务运行时各种其他内存空间的需要。

spark.memory.storageFraction默认值为0.5,官方文档建议这个比值不要超过JVM Old Gen区域的比值。这也很容易理解,因为RDD Cache数据通常都是长期驻留内存的,理论上也就是说最终会被转移到Old Gen区域(如果该RDD还没有被删除的话),如果这部分数据允许的尺寸太大,势必把Old Gen区域占满,造成频繁的FULL GC。

如何调整这个比值,取决于你的应用对数据的使用模式和数据的规模,粗略的来说,如果频繁发生Full GC,可以考虑降低这个比值,这样RDD Cache可用的内存空间减少(剩下的部分Cache数据就需要通过Disk Store写到磁盘上了),会带来一定的性能损失,但是腾出更多的内存空间用于执行任务,减少Full GC发生的次数,反而可能改善程序运行的整体性能。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈