下图显示的是Spark 1.6.x 以前版本对 Java 堆 (heap) 的使用情况,左则是 Storage 对内存的使用,右则是 Shuffle 对内存的使用,这叫 StaticMemoryManagement,数据处理以及类的实体对象都存放在 JVM 堆 (heap) 中。
[下图是 Spark 1.6x 以前版本对 JVM 堆 Storage 和 Shuffle 的使用分布]
Spark 1.6.x 版本对 JVM 堆的使用
JVM Heap 默认情况下是 512MB,这是取决于 spark.executor.memory 的参数,在回答 Spark JVM 到底可以缓存多少数据这个问题之前,首先了解一下 JVM Heap 在 Spark 中是如何分配内存比例的。无论你定义了 spark.executor.memory 的内存空间有多大,Spark 必然会定义一个安全空间,在默认情况下只会使用 Java 堆上的 90% 作为安全空间,在单个 Executor 的角度来讲,就是 Heap Size x 90%。
埸景一:假设说在一个Executor,它可用的 Java Heap 大小是 10G,实际上 Spark 只能使用 90%,这个安全空间的比例是由 spark.storage.safetyFaction 来控制的。(如果你内存的 Heap 非常大的话,可以尝试调高为 95%),在安全空间中也会划分三个不同的空间:一个是 Storage 空间、一个是 Unroll 空间和一个是 Shuffle 空间。
安全空间 (safe):计算公式是 spark.executor.memory x spark.storage.safetyFraction。也就是 Heap Size x 90%,在埸景一的例子中是 10 x 0.9 = 9G;
缓存空间 (Storage):计算公式是 spark.executor.memory x spark.storage.safetyFraction x spark.storage.memoryFraction。也就是 Heap Size x 90% x 60%;Heap Size x 54%,在埸景一的例子中是 10 x 0.9 x 0.6 = 5.4G;一个应用程序可以缓存多少数据是由 safetyFraction 和 memoryFraction 这两个参数共同决定的。
[下图是 StaticMemoryManager.scala 中的 getMaxStorageMemory 方法]
Unroll 空间:
计算公式是 spark.executor.memory x spark.storage.safetyFraction x spark.storage.memoryFraction x spark.storage.unrollFraction
也就是 Heap Size x 90% x 60% x 20%;Heap Size x 10.8%,在埸景一的例子中是 10 x 0.9 x 0.6 x 0.2 = 1.8G,你可能把序例化后的数据放在内存中,当你使用数据时,你需要把序例化的数据进行反序例化。
[下图是 StaticMemoryManager.scala 中的 maxUnrollMemory 变量]
对 cache 缓存数据的影响是由于 Unroll 是一个优先级较高的操作,进行 Unroll 操作的时候会占用 cache 的空间,而且又可以挤掉缓存在内存中的数据 (如果该数据的缓存级别是 MEMORY_ONLY 的话,否则该数据会丢失)。
Shuffle 空间:计算公式是 spark.executor.memory x spark.shuffle.memoryFraction x spark.shuffle.safteyFraction。在 Shuffle 空间中也会有一个默认 80% 的安全空间比例,所以应该是 Heap Size x 20% x 80%;Heap Size x 16%,在埸景一的例子中是 10 x 0.2 x 0.8 = 1.6G;从内存的角度讲,你需要从远程抓取数据,抓取数据是一个 Shuffle 的过程,比如说你需要对数据进行排序,显现在这个过程中需要内存空间。
[下图是 StaticMemoryManager.scala 中的 getMaxExecutionMemory 方法]