spark性能调优(四) spark shuffle中JVM内存使用及配置内幕详情

转载:http://www.cnblogs.com/jcchoiling/p/6494652.htmlhtml

引言

Spark 从1.6.x 开始对 JVM 的内存使用做出了一种全新的改变,Spark 1.6.x 之前是基于静态固定的JVM内存使用架构和运行机制,若是你不知道 Spark 到底对 JVM 是怎么使用,你怎么能够颇有信心地或者是彻底肯定地掌握和控制数据的缓存空间呢,因此掌握Spark对JVM的内存使用内幕是相当重要的。不少人对 Spark 的印象是:它是基于内存的,并且能够缓存一大堆数据,显现 Spark 是基于内存的观点是错的,Spark 只是优先充分地利用内存而已。若是你不知道 Spark 能够缓存多少数据,你就误乱地缓存数据的话,确定会有问题。算法

在数据规模已经肯定的状况下,你有多少 Executor 和每一个 Executor 可分配多少内存 (在这个物理硬件已经肯定的状况下),你必须清楚知道你的內存最多可以缓存多少数据;在 Shuffle 的过程当中又使用了多少比例的缓存,这样对于算法的编写以及业务实现是相当重要的!!!缓存

文章的后部份会介绍 Spark 2.x 版本 JVM 的内存使用比例,它被称之为 Spark Unified Memory,这是统一或者联合的意思,可是 Spark 没有用 Shared 这个字,由于 A 和 B 进行 Unified 和 A 和 B 进行 Shared 实际上是两个不一样的概念, Spark 在运行的时候会有不一样类型的 OOM,你必须搞清楚这个 OOM 背后是由什么致使的。 好比说咱们使用算子 mapPartition 的时候,通常会建立一些临时对象或者是中间数据,你这个时候使用的临时对象和中间数据,是存储在一个叫 UserSpace 里面的用户操做空间,那你有没有想过这个空间的大小会致使应用程序出现 OOM 的状况,在 Spark 2.x 中 Broadcast 的数据是存储在什么地方;ShuffleMapTask 的数据又存储在什么地方,可能你会认为 ShuffleMapTask 的数据是缓存在 Cache 中。这篇文章会介绍 JVM 在 Spark 1.6.X 之前和 2.X 版本对 Java 堆的使用,还会逐一解密上述几个疑问,也会简单介绍 Spark 1.6.x 之前版本在 Spark On Yarn 上内存的使用案例,但愿这篇文章能为读者带出如下的启发: 安全

  • 了解 JVM 内存使用架构剖析 
  • 了解 JVM 在 Spark 1.6.x 之前和 Spark 2.x 中能够缓存多少数据
  • 了解 Spark Unified Memory 的原理与机制还有它三大核心空间的用途
  • 了解 Shuffle 在 Spark 1.6.x 之前和 Spark 2.x 中可使用多少缓存
  • 了解 Spark1.6.x 之前 on Yarn 对内存的使用
  • 了解 在 Spark 1.6.x 之前和 Spark 2.x Shuffle 的参数配置

1、JVM内存使用架构剖析

JVM 有不少不一样的区,最开始的时候,它会经过类装载器把类加载进来,在运行期数据区中有 "本地方法栈","程序计数器","Java 栈"、"Java 堆"和"方法区"以及本地方法接口和它的本地库。从 Spark 的角度来谈代码的运行和数据的处理,主要是谈 Java 堆 (Heap) 空间的运用。架构

  • 本地方法栈:这个是在迭归的时候确定是相当重要的;
  • 程序计数器:这是一个全区计数器,对于线程切换是相当重要的;
  • Java 栈 (Stack)Stack 区属于线程私有,高效的程序通常都是并发的,每一个线程都会包含一个 Stack 区域,Stack 区域中含有基本的数据类型以及对象的引用,其它线程均不能直接访问该区域;Java 栈分为三大部份:基本数据类型区域、操做指令区域、上下文等;
  • Java 堆 (Heap):存储的所有都是 Object 对象实例,对象实例中通常都包含了其数据成员以及与该对象对应类的信息,它会指向类的引用一个,不一样线程确定要操做这个对象;一个 JVM 实例在运行的时候只有一个 Heap 区域,并且该区域被全部的线程共享;补充说明:垃圾回收是回收堆 (heap) 中内容,堆上才有咱们的对象
  • 方法区:又名静态成员区域,包含整个程序的 class、static 成员等,类自己的字节码是静态的;它会被全部的线程共享和是全区级别的

2、Spark 1.6.x 和 2.x 的 JVM 剖析

     一、Spark JVM 到底能够缓存多少数据并发

下图显示的是Spark 1.6.x 之前版本对 Java 堆 (heap) 的使用状况,左则是 Storage 对内存的使用,右则是 Shuffle 对内存的使用,这叫 StaticMemoryManagement,数据处理以及类的实体对象都存放在 JVM 堆 (heap) 中。spa

    二、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%。htm

埸景一:假设说在一个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 这两个参数共同决定的。

     

       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,你可能把序例化后的数据放在内存中,当你使用数据时,你须要把序例化的数据进行反序例化。

       

对 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 的过程,好比说你须要对数据进行排序,显如今这个过程当中须要内存空间。

     三、Spark Unified Memory 原理和运行机制

下图是一种叫联合内存 (Spark Unified Memeory),数据缓存与数据执行之间的内存能够相互移动,这是一种更弹性的方式,下图显示的是 Spark 2.x 版本对 Java 堆 (heap) 的使用状况,数据处理以及类的实体对象存放在 JVM 堆 (heap) 中。

相关文章
相关标签/搜索