首页 文章

Spark java.lang.OutOfMemoryError:Java堆空间

提问于
浏览
172

我的集群:1个主服务器,11个从服务器,每个节点有6 GB内存 .

我的设置:

spark.executor.memory=4g, Dspark.akka.frameSize=512

Here is the problem:

First ,我从HDFS到RDD读取了一些数据(2.19 GB):

val imageBundleRDD = sc.newAPIHadoopFile(...)

Second ,在这个RDD上做点什么:

val res = imageBundleRDD.map(data => {
                               val desPoints = threeDReconstruction(data._2, bg)
                                 (data._1, desPoints)
                             })

Last ,输出到HDFS:

res.saveAsNewAPIHadoopFile(...)

当我运行我的程序时,它显示:

.....
14/01/15 21:42:27 INFO cluster.ClusterTaskSetManager: Starting task 1.0:24 as TID 33 on executor 9: Salve7.Hadoop (NODE_LOCAL)
14/01/15 21:42:27 INFO cluster.ClusterTaskSetManager: Serialized task 1.0:24 as 30618515 bytes in 210 ms
14/01/15 21:42:27 INFO cluster.ClusterTaskSetManager: Starting task 1.0:36 as TID 34 on executor 2: Salve11.Hadoop (NODE_LOCAL)
14/01/15 21:42:28 INFO cluster.ClusterTaskSetManager: Serialized task 1.0:36 as 30618515 bytes in 449 ms
14/01/15 21:42:28 INFO cluster.ClusterTaskSetManager: Starting task 1.0:32 as TID 35 on executor 7: Salve4.Hadoop (NODE_LOCAL)
Uncaught error from thread [spark-akka.actor.default-dispatcher-3] shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled for ActorSystem[spark]
java.lang.OutOfMemoryError: Java heap space

任务太多了?

PS :当输入数据大约为225 MB时,每件事情都可以 .

我怎么解决这个问题?

7 回答

  • 43

    看看the start up scripts那里设置了Java堆大小,看起来你没有在运行Spark worker之前设置它 .

    # Set SPARK_MEM if it isn't already set since we also use it for this process
    SPARK_MEM=${SPARK_MEM:-512m}
    export SPARK_MEM
    
    # Set JAVA_OPTS to be able to load native libraries and to set heap size
    JAVA_OPTS="$OUR_JAVA_OPTS"
    JAVA_OPTS="$JAVA_OPTS -Djava.library.path=$SPARK_LIBRARY_PATH"
    JAVA_OPTS="$JAVA_OPTS -Xms$SPARK_MEM -Xmx$SPARK_MEM"
    

    您可以找到部署脚本here的文档 .

  • 14

    你应该增加驱动程序内存 . 在你的$ SPARK_HOME / conf文件夹中,你应该找到文件 spark-defaults.conf ,编辑并设置 spark.driver.memory 4000m ,具体取决于你的主机上的内存,我想 . 这就是我解决问题的方法,一切顺利

  • 13

    您应该配置offHeap内存设置,如下所示:

    `val spark = SparkSession
         .builder()
         .master("local[*]")
         .config("spark.executor.memory", "70g")
         .config("spark.driver.memory", "50g")
         .config("spark.memory.offHeap.enabled",true)
         .config("spark.memory.offHeap.size","16g")   
         .appName("sampleCodeForReference")
         .getOrCreate()`
    

    根据您的计算机RAM可用性为驱动程序提供内存和执行程序内存 . You can increase the offHeap size if you are still facing the OutofMemory issue .

  • 279

    我有几点建议:

    • 如果您的节点配置为Spark的最大值为6g(并为其他进程留下一点),则使用6g而不是4g, spark.executor.memory=6g . 通过检查用户界面来确保 you're using as much memory as possible (它会说明你正在使用多少内存)

    • 尝试使用更多分区,每个CPU应该有2到4个 . IME增加分区数量通常是使程序更稳定(通常更快)的最简单方法 . 对于大量数据,每个CPU可能需要4个以上的数据,在某些情况下我不得不使用8000个分区!

    • 使用 spark.storage.memoryFraction 减少 fraction of memory reserved for caching . 如果你的代码中没有使用 cache()persist ,那么它也可能是0.它的默认值是0.6,这意味着你的堆只能获得0.4 * 4g的内存 . IME减少内存压力通常会使OOM消失 . UPDATE: 从火花1.6显然我们将不再需要玩这些值,火花将自动确定它们 .

    • 与上述类似,但 shuffle memory fraction . 如果你的工作没有't need much shuffle memory then set it to a lower value (this might cause your shuffles to spill to disk which can have catastrophic impact on speed). Sometimes when it'一个shuffle操作's OOMing you need to do the opposite i.e. set it to something large, like 0.8, or make sure you allow your shuffles to spill to disk (it'是自1.0.0以来的默认值 .

    • 注意 memory leaks ,这些通常是由于意外地关闭了lambda中不需要的物体 . 诊断的方法是在日志中查找"task serialized as XXX bytes",如果XXX大于几k或大于MB,则可能存在内存泄漏 . 见https://stackoverflow.com/a/25270600/1586965

    • 与上述有关;如果你确实需要大型对象,请使用 broadcast variables .

    • 如果您正在缓存大型RDD并且可以牺牲一些访问时间,请考虑序列化RDD http://spark.apache.org/docs/latest/tuning.html#serialized-rdd-storage . 甚至可以将它们缓存在磁盘上(如果使用SSD,有时也不会那么糟糕) .

    • Advanced )与上述相关,避免 String 和重度嵌套的结构(如 Map 和嵌套的案例类) . 如果可能,尝试仅使用原始类型并索引所有非基元,特别是如果您期望大量重复 . 尽可能在嵌套结构上选择 WrappedArray . 或者甚至推出自己的序列化 - 您将获得有关如何有效地将数据备份为字节的最多信息, USE IT

    • bit hacky )再次缓存时,请考虑使用 Dataset 缓存结构,因为它将使用更高效的序列化 . 与前一个要点相比,这应被视为黑客攻击 . 将您的领域知识构建到您的算法/序列化中可以将内存/缓存空间最小化100倍或1000倍,而所有 Dataset 可能会在内存中提供2x - 5x,在磁盘上提供10x压缩(镶木地板) .

    http://spark.apache.org/docs/1.2.1/configuration.html

    编辑:(所以我可以更容易谷歌)以下也表明了这个问题:

    java.lang.OutOfMemoryError : GC overhead limit exceeded
    
  • 2

    要向此添加一个通常没有讨论的用例,我将在 local 模式下通过 spark-submit 提交 Spark 应用程序时提出解决方案 .

    根据Mastering Apache Spark的gitbook Mastering Apache Spark

    您可以在本地模式下运行Spark . 在这种非分布式单JVM部署模式中,Spark在同一个JVM中生成所有执行组件 - 驱动程序,执行程序,后端和主服务器 . 这是驱动程序用于执行的唯一模式 .

    因此,如果您遇到 heapOOM 错误,则只需调整 driver-memory 而不是 executor-memory .

    这是一个例子:

    spark-1.6.1/bin/spark-submit
      --class "MyClass"
      --driver-memory 12g
      --master local[*] 
      target/scala-2.10/simple-project_2.10-1.0.jar
    
  • 3

    从广义上讲,spark Executor JVM内存可以分为两部分 . 火花记忆和用户记忆 . 这由属性 spark.memory.fraction 控制 - 值介于0和1之间 . 在处理图像或在spark应用程序中执行内存密集型处理时,请考虑减小 spark.memory.fraction . 这将为您的应用程序工作提供更多内存 . Spark可以溢出,因此它仍然可以用较少的内存共享 .

    问题的第二部分是分工 . 如果可能,将数据分区为较小的块 . 较小的数据可能需要较少的内存 . 但如果那是不可能的,那么你牺牲计算记忆 . 通常,单个执行程序将运行多个核心 . 执行程序的总内存必须足以处理所有并发任务的内存要求 . 如果不能增加执行程序内存,则可以减少每个执行程序的内核,以便每个任务都可以获得更多内存 . 使用1个核心执行程序进行测试,这些执行程序可以提供最大可能的内存,然后继续增加核心,直到找到最佳核心数 .

  • 4

    设置内存堆大小的位置(至少在spark-1.0.0中)位于conf / spark-env中 . 相关变量是 SPARK_EXECUTOR_MEMORYSPARK_DRIVER_MEMORY . deployment guide中有更多文档

    另外,不要忘记将配置文件复制到所有从属节点 .

相关问题