Spark java.lang.OutOfMemoryError:Java堆空间

时间:2014-01-15 13:30:51

标签: out-of-memory apache-spark

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

我的设置:

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

以下是问题:

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

val imageBundleRDD = sc.newAPIHadoopFile(...)

第二次,在此RDD上执行某些操作:

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

上次,输出到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时,每件事情都可以。

我该如何解决这个问题?

13 个答案:

答案 0 :(得分:310)

我有一些建议:

  • 如果您的节点配置为Spark的最大值为6g(并为其他进程留下一点),则使用6g而不是4g spark.executor.memory=6g。通过检查用户界面确保你正在使用尽可能多的内存(它会说明你正在使用多少内存)
  • 尝试使用更多分区,每个CPU应该有2到4个。 IME增加分区数量通常是使程序更稳定(通常更快)的最简单方法。对于大量数据,您可能需要每个CPU超过4个,在某些情况下我不得不使用8000个分区!
  • 使用spark.storage.memoryFraction减少为缓存保留的内存部分。如果你的代码中没有使用cache()persist,那么它也可能是0.它的默认值是0.6,这意味着你的堆只能得到0.4 * 4g的内存。 IME减少内存压力通常会使OOM消失。 更新:从火花1.6开始,我们将不再需要使用这些值,火花将自动确定它们。
  • 与上述类似,但随机内存分数。如果你的工作不需要太多的shuffle内存,那么将它设置为较低的值(这可能会导致你的shuffle溢出到磁盘,这可能会对速度造成灾难性的影响)。有时当它是一个随机操作时,你需要做相反的操作,即将它设置为大的,比如0.8,或者确保你允许你的shuffle溢出到磁盘(这是1.0.0以来的默认值)。
  • 注意内存泄漏,这些通常是由意外关闭lambda中不需要的对象引起的。诊断的方法是在日志中查找“序列化为XXX字节的任务”,如果XXX大于几k或大于MB,则可能存在内存泄漏。请参阅https://stackoverflow.com/a/25270600/1586965
  • 与上述有关;如果确实需要大型对象,请使用广播变量
  • 如果您正在缓存大型RDD并且可以牺牲一些访问时间,请考虑序列化RDD http://spark.apache.org/docs/latest/tuning.html#serialized-rdd-storage。甚至可以将它们缓存在磁盘上(如果使用SSD,有时也不会那么糟糕)。
  • 高级)与上述相关,请避免使用String和重度嵌套的结构(如Map和嵌套的案例类)。如果可能,尝试仅使用原始类型并索引所有非基元,特别是如果您期望大量重复。尽可能在嵌套结构上选择WrappedArray。或者甚至推出自己的序列化 - 您将获得有关如何有效地将数据备份为字节的最多信息,使用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

答案 1 :(得分:48)

要向此添加一个通常没有讨论过的用例,我会在本地模式下通过Spark提交spark-submit申请时提出解决方案。

根据Mastering Apache Spark的gitbook Jacek Laskowski

  

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

因此,如果您遇到OOM的{​​{1}}错误,则只需调整heap而不是driver-memory

以下是一个例子:

executor-memory

答案 2 :(得分:14)

看看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的文档。

答案 3 :(得分:14)

您应该增加驱动程序内存。在你的$ SPARK_HOME / conf文件夹中,你应该找到文件spark-defaults.conf,根据主人的内存编辑和设置spark.driver.memory 4000m,我想。 这就是为我解决问题并且一切顺利进行的原因

答案 4 :(得分:7)

您应该配置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可用性为驱动程序提供内存和执行程序内存。 如果您仍然面临OutofMemory问题,可以增加offHeap尺寸

答案 5 :(得分:4)

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

问题的第二部分是分工。如果可能,将数据分成较小的块。较小的数据可能需要较少的内存。但是,如果这不可能,那么您将牺牲内存的计算能力。通常,一个执行程序将运行多个内核。执行程序的总内存必须足以处理所有并发任务的内存需求。如果不能增加执行程序的内存,则可以减少每个执行程序的内核,以便每个任务都可以使用更多的内存。 使用1个具有最大可能内存的核心执行程序进行测试,然后不断增加核心,直到找到最佳核心数量。

答案 6 :(得分:4)

您是否转储了主gc日志?所以我遇到了类似的问题,我发现SPARK_DRIVER_MEMORY只设置了Xmx堆。初始堆大小保持为1G,并且堆大小永远不会扩展到Xmx堆。

传递“ --conf” spark.driver.extraJavaOptions = -Xms20g“解决了我的问题。

ps aux | grep java,您将看到以下日志:=

24501 30.7 1.7 41782944 2318184 pts / 0 Sl + 18:49 0:33 / usr / java / latest / bin / java -cp / opt / spark / conf /:/ opt / spark / jars / * -Xmx30g -Xms20g

答案 7 :(得分:3)

这个问题让我非常痛苦,我们使用动态资源分配,我认为它将利用我的群集资源来最适合该应用程序。

但是事实是,动态资源分配不会设置驱动程序内存,而是将其保留为默认值1g。

我已通过将spark.driver.memory设置为适合我的驱动程序内存的数字(对于32gb的内存,我将其设置为18gb)来解决了该问题

您可以使用spark提交命令进行设置,如下所示:

spark-submit --conf spark.driver.memory=18gb ....cont

非常重要的一点是,根据spark文档,如果您通过代码进行设置,则不会考虑该属性:

  

Spark属性主要可以分为两种:一种与部署相关,例如“ spark.driver.memory”,“ spark.executor.instances”,当通过SparkConf进行编程设置时,此类属性可能不会受到影响。运行时,或行为取决于选择的集群管理器和部署模式,因此建议通过配置文件或spark-submit命令行选项进行设置;另一个主要与Spark运行时控件有关,例如“ spark.task.maxFailures”,可以用任何一种方式设置这种属性。

答案 8 :(得分:2)

设置内存堆大小的位置(至少在spark-1.0.0中)位于conf / spark-env中。 相关变量为SPARK_EXECUTOR_MEMORY& SPARK_DRIVER_MEMORY。 更多文档位于deployment guide

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

答案 9 :(得分:1)

对于上述错误,我几乎没有建议。

●检查分配为执行程序的执行程序内存可能必须处理需要比分配的内存更多的分区。

●尝试查看是否有更多的shuffle,因为shuffle是昂贵的操作,因为它们涉及磁盘I / O,数据序列化和网络I / O

●使用广播加入

●避免使用groupByKeys并尝试将其替换为ReduceByKey

●避免在发生改组的地方使用大型Java对象

答案 10 :(得分:1)

设置这些确切的配置有助于解决此问题。

spark-submit --conf spark.yarn.maxAppAttempts=2 --executor-memory 10g --num-executors 50 --driver-memory 12g

答案 11 :(得分:0)

据我对上面提供的代码的理解,它加载文件并执行映射操作并将其保存回去。没有需要洗牌的操作。另外,由于没有需要将数据带给驱动程序的操作,因此调整与随机播放或驱动程序相关的任何内容都不会产生影响。当任务太多时,驱动程序确实有问题,但这只是在spark 2.0.2版本之前。可能有两件事出了问题。

  • 只有一个或几个执行者。增加执行程序的数量,以便可以将其分配给不同的从站。如果您正在使用yarn需要更改num-executors配置,或者您正在独立使用spark,则需要调整每个执行器的num cores和spark max cores conf。在独立的num executors =最大核心数/每个执行者核心数的情况下。
  • 分区的数量很少,或者也许只有一个。因此,即使我们有多核,多执行器,这仍然很低,因为并行化取决于分区的数量,因此不会有太大帮助。因此,通过执行imageBundleRDD.repartition(11)来增加分区

答案 12 :(得分:0)

堆空间错误通常是由于将太多数据带回驱动程序或执行程序而发生的。 在您的代码中,似乎并没有将任何东西带回驱动程序,但是您可能会使用ThreeDReconstruction()方法来重载将输入记录/行映射到另一个记录的执行程序。我不确定方法定义中包含什么,但这肯定会导致执行程序超载。 现在您有2个选择,

  1. 编辑代码,以更有效的方式进行3-D重建。
  2. 不执行任何编辑代码,但给执行者更多的内存,以及更多的内存开销。 [spark.executor.memory或spark.driver.memoryOverhead]

我建议增加使用量时要小心,并且仅在需要时使用。就内存需求而言,每个作业都是唯一的,因此我建议您凭经验建议每次尝试将不同的值增加2的幂(256M,512M,1G等)

您将获得将起作用的执行程序内存的值。尝试使用此值重新运行作业3或5次,然后再设置此配置。