266

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

我的设置:

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

这是问题所在:

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

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 时,一切正常。

我怎么解决这个问题?

4

13 回答 13

422

我有几个建议:

  • 如果您的节点被配置为 Spark 的最大容量为 6g(并且为其他进程保留了一点),那么请使用 6g 而不是 4g spark.executor.memory=6g,. 通过检查 UI确保您使用尽可能多的内存(它会显示您正在使用多少内存)
  • 尝试使用更多分区,每个 CPU 应该有 2 - 4 个。IME 增加分区数量通常是使程序更稳定(通常更快)的最简单方法。对于海量数据,每个 CPU 可能需要超过 4 个,在某些情况下我不得不使用 8000 个分区!
  • 减少为缓存保留的内存比例,使用spark.storage.memoryFraction. 如果你没有在你的代码中使用cache()or persist,这也可能是 0。它的默认值为 0.6,这意味着你的堆只能获得 0.4 * 4g 内存。IME 减少 mem frac 通常会使 OOM 消失。更新:从 spark 1.6 开始,我们将不再需要使用这些值,spark 将自动确定它们。
  • 与上面类似,但shuffle memory fraction。如果您的工作不需要太多随机播放内存,请将其设置为较低的值(这可能会导致您的随机播放溢出到磁盘,这可能会对速度产生灾难性影响)。有时,当它是 OOM 的随机播放操作时,您需要执行相反的操作,即将其设置为较大的值,例如 0.8,或者确保您允许随机播放溢出到磁盘(这是自 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尽可能选择嵌套结构。甚至推出您自己的序列化 - 您将获得有关如何有效地将数据备份为字节的最多信息,请使用它
  • 有点骇人听闻)再次缓存时,请考虑使用 aDataset来缓存您的结构,因为它将使用更有效的序列化。与前面的要点相比,这应该被视为一种 hack。将您的领域知识构建到您的算法/序列化中可以将内存/缓存空间减少 100 倍或 1000 倍,而所有 aDataset可能会提供 2 倍至 5 倍的内存和 10 倍的磁盘压缩(镶木地板)。

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

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

java.lang.OutOfMemoryError : GC overhead limit exceeded
于 2014-03-30T10:43:16.610 回答
72

为了添加一个经常不讨论的用例,我将在本地模式下提交Spark应用程序时提出一个解决方案。spark-submit

根据Jacek Laskowski的gitbook Mastering Apache Spark

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

因此,如果您在使用 时遇到OOM错误heap,只需调整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 
于 2016-03-12T18:56:43.370 回答
32

您应该配置 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 大小

于 2018-06-11T14:50:31.520 回答
17

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

于 2015-09-03T21:15:15.813 回答
15

看看那里设置了 Java 堆大小的启动脚本,看起来你在运行 Spark 工作程序之前没有设置这个。

# 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"

您可以在此处找到部署脚本的文档。

于 2014-01-16T09:03:11.893 回答
11

在使用动态资源分配时,我经常遇到这个问题。我原以为它会利用我的集群资源来最适合应用程序。

但事实是动态资源分配并没有设置驱动程序内存,而是将其保持为默认值,即 1G。

我通过设置spark.driver.memory一个适合我的驱动程序内存的数字解决了这个问题(对于 32GB 内存,我将它设置为 18G)。

您可以使用 spark submit 命令设置它,如下所示:

spark-submit --conf spark.driver.memory=18g

非常重要的一点是,根据Spark 文档 - 动态加载 Spark 属性,如果您从代码中设置它,则不会考虑此属性:

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

于 2018-12-27T09:09:43.363 回答
6

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

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

于 2018-07-03T06:52:26.973 回答
5

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

通过 "--conf "spark.driver.extraJavaOptions=-Xms20g" 解决了我的问题。

ps辅助| 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

于 2019-08-08T20:15:21.043 回答
4

设置内存堆大小的位置(至少在 spark-1.0.0 中)在 conf/spark-env 中。相关变量是SPARK_EXECUTOR_MEMORY& SPARK_DRIVER_MEMORY部署指南中有更多文档

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

于 2014-08-06T11:33:10.273 回答
3

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

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

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

● 使用广播连接

● 避免使用 groupByKeys 并尝试用 ReduceByKey 代替

● 避免在发生洗牌时使用巨大的 Java 对象

于 2019-02-13T10:39:11.107 回答
1

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

  • 只有一个或几个执行者。增加executor的数量,以便分配给不同的slave。如果您使用 yarn 需要更改 num-executors 配置,或者如果您使用 spark 独立,则需要调整每个执行器的 num cores 和 spark max cores conf。在独立 num executors = max cores / cores per executor 中。
  • 分区的数量很少,或者可能只有一个。因此,即使我们有多核、多执行器,这也不会有太大帮助,因为并行化取决于分区的数量。所以通过执行 imageBundleRDD.repartition(11) 来增加分区
于 2019-10-22T05:13:01.863 回答
0

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

  1. 编辑您的代码以更有效的方式进行 3-D 重建。
  2. 不编辑代码,但为执行程序提供更多内存,并提供更多内存开销。[spark.executor.memory 或 spark.driver.memoryOverhead]

我建议小心增加,只根据需要使用。每个作业在内存要求方面都是独一无二的,所以我建议根据经验尝试不同的值,每次增加 2 的幂(256M、512M、1G .. 等等)

您将获得一个可以工作的执行程序内存值。在适应此配置之前,尝试使用此值重新运行作业 3 或 5 次。

于 2020-12-01T01:43:29.500 回答
-1

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

spark-submit --conf spark.yarn.maxAppAttempts=2 --executor-memory 10g --num-executors 50 --driver-memory 12g
于 2019-11-22T03:52:57.610 回答