Spark java.lang.OutOfMemoryError:Java 堆空间

Posted

技术标签:

【中文标题】Spark java.lang.OutOfMemoryError:Java 堆空间【英文标题】:Spark java.lang.OutOfMemoryError: Java heap space 【发布时间】:2014-02-04 00:00:38 【问题描述】:

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

我的设置:

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

我该如何解决这个问题?

【问题讨论】:

如何运行spark?它来自控制台吗?或者您使用哪些部署脚本? 我使用 sbt 编译和运行我的应用程序。 sbt 包然后 sbt 运行。一个月前我在hadoop上实现了同样的程序,遇到了同样的OutOfMemoryError问题,但是在hadoop中可以通过将mapred.child.java.opts的值从Xmx200m增加到Xmx400m来轻松解决。 spark是否对其任务有任何jvm设置?我想知道spark.executor.memory是否与hadoop中的mapred.child.java.opts含义相同。在我的程序中 spark.executor.memory 已经设置为 4g,比 hadoop 中的 Xmx400m 大得多。谢谢~ 你提到的三个步骤是你唯一做的吗?由 (data._1, desPoints) 生成的数据的大小是多少 - 这应该适合内存,尤其是如果这些数据被洗牌到另一个阶段 驱动的内存配置是什么?检查哪个服务器出现内存不足错误。是司机还是执行人之一。 在这里查看所有配置属性:spark.apache.org/docs/2.1.0/configuration.html 【参考方案1】:

我有几点建议:

如果您的节点配置为 Spark 的最大容量为 6g(并且为其他进程保留了一点),则使用 6g 而不是 4g,spark.executor.memory=6g。通过检查 UI 确保您正在使用尽可能多的内存(它会显示您正在使用多少内存) 尝试使用更多分区,每个 CPU 应该有 2 - 4 个。 IME 增加分区数量通常是使程序更稳定(通常更快)的最简单方法。对于海量数据,每个 CPU 可能需要超过 4 个,在某些情况下我不得不使用 8000 个分区! 使用spark.storage.memoryFraction 减少为缓存保留的内存部分。如果您的代码中没有使用cache()persist,这也可能是0。默认为0.6,这意味着您的堆只能获得0.4 * 4g 内存。 IME 减少 mem frac 通常会使 OOM 消失。 更新:从 spark 1.6 开始,我们将不再需要使用这些值,spark 会自动确定它们。 与上述类似,但随机播放内存部分。如果您的工作不需要太多随机播放内存,请将其设置为较低的值(这可能会导致您的随机播放溢出到磁盘,这可能会对速度产生灾难性影响)。有时,当它是 OOM 的 shuffle 操作时,您需要执行相反的操作,即将其设置为较大的值,例如 0.8,或者确保允许您的 shuffle 溢出到磁盘(这是自 1.0.0 以来的默认设置)。 注意内存泄漏,这些通常是由于意外关闭了 lambda 中不需要的对象造成的。诊断的方法是在日志中寻找“任务序列化为XXX字节”,如果XXX大于几k或大于MB,则可能存在内存泄漏。见https://***.com/a/25270600/1586965 与上述相关;如果您确实需要大型对象,请使用广播变量。 如果您正在缓存大型 RDD 并且可能会牺牲一些访问时间,请考虑将 RDD http://spark.apache.org/docs/latest/tuning.html#serialized-rdd-storage 序列化。甚至将它们缓存在磁盘上(如果使用 SSD,这有时还不错)。 (高级)与上述相关,避免使用String 和重度嵌套结构(如Map 和嵌套案例类)。如果可能,请尝试仅使用原始类型并索引所有非原始类型,尤其是在您期望有很多重复项的情况下。尽可能选择WrappedArray 而不是嵌套结构。甚至推出您自己的序列化 - 您将获得有关如何有效地将数据备份为字节的最多信息,使用它! (bit hacky) 再次缓存时,请考虑使用Dataset 来缓存您的结构,因为它将使用更有效的序列化。与前面的要点相比,这应该被视为一种 hack。将您的领域知识构建到您的算法/序列化中可以将内存/缓存空间减少 100 倍或 1000 倍,而 Dataset 可能会提供 2 倍至 5 倍的内存和 10 倍的磁盘压缩(镶木地板)。

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

编辑:(这样我可以更轻松地自己google)以下也表明了这个问题:

java.lang.OutOfMemoryError : GC overhead limit exceeded

【讨论】:

谢谢你的建议~如果我设置spark.executor.memory=6g,spark会有问题:“检查你的集群UI,确保workers已经注册并且有足够的内存”。将 spark.storage.memoryFraction 设置为 0.1 也不能解决问题。也许问题出在我的代码上。谢谢! @samthebest 这是一个很棒的答案。我非常感谢日志记录对查找内存泄漏的帮助。 嗨@samthebest你是如何指定8000个分区的?由于我使用的是 Spark sql,我只能使用 spark.sql.shuffle.partitions 指定分区,默认值为 200 我应该将其设置为更多 我尝试将其设置为 1000 但无助于获得 OOM 你知道什么应该是最佳的分区值我有 1 TB 倾斜数据要处理,它涉及按配置单元查询分组。请指导。 嗨@user449355,你能问一个新问题吗?因为害怕开始一个长的评论线程:)如果你有问题,可能其他人也有,一个问题会让所有人更容易找到。 首先,@samthebest,您不应该将所有内存用于spark.executor.memory,因为您肯定需要一些内存用于 I/O 开销。如果你全部使用它,它会减慢你的程序。 Unix 可能是个例外,在这种情况下你有交换空间。【参考方案2】:

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

根据 gitbook Mastering Apache Spark by Jacek Laskowski:

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

因此,如果您在使用heap 时遇到OOM 错误,只需调整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 

【讨论】:

我们应该为独立模式下的驱动程序内存考虑多少百分比。 @Brian,在本地模式下,驱动内存是否需要大于输入数据大小?是否可以为输入数据集指定分区数,以便 Spark 作业可以处理比可用 RAM 大得多的数据集? 驱动内存不能大于输入大小。假设您有一个 160gb 的文件要加载到集群中。那么,为此,您将创建一个 161 GB 的驱动程序?这是不可行的。它是您如何确定执行器的数量、它们的内存以及开销内存及其操作系统的缓冲区。您需要通过查看 yarn UI 和给您的集群内存来计算所有这些东西。为了获得更好的性能,您还需要考虑 executor-cores 应该始终在 3-5 @fuyi【参考方案3】:

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

【讨论】:

添加了 offHeap 设置帮助 在你的代码中设置驱动内存是行不通的,请阅读spark文档了解一下: Spark属性主要可以分为两种:一种和deploy相关的,比如“spark.driver.memory”, “spark.executor.instances”,这种属性在运行时通过SparkConf编程设置可能不会受到影响,或者行为取决于你选择的集群管理器和部署模式,所以建议通过配置文件或spark-submit 命令行选项。 最佳答案!我的问题是主节点上没有安装 Spark,我只是使用 PySpark 连接到 HDFS 并得到了同样的错误。使用config 解决了这个问题。 我刚刚使用 spark-submit 命令添加了配置来修复堆大小问题。谢谢。【参考方案4】:

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

【讨论】:

单独分配多少百分比的内存【参考方案5】:

看看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。

【讨论】:

谢谢~我稍后再试试。从spark ui看,每个executor的内存都是4096,所以设置已经开启了吧? 在我遇到类似问题时看到了您的答案 (***.com/questions/34762432/…)。看看你提供的链接,好像设置 Xms/Xmx 已经不存在了,你能说出原因吗? 不幸的是,start up scripts 链接的脚本中的内容已更改。截至 2019 年 12 月 19 日,不存在此类选项【参考方案6】:

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

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

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

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

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

非常重要的一点,如果你从代码中设置,这个属性将不会被考虑,根据Spark Documentation - Dynamically Loading Spark Properties:

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

【讨论】:

你应该使用 --conf spark.driver.memory=18g【参考方案7】:

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

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

【讨论】:

【参考方案8】:

您是否转储了您的主 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

【讨论】:

【参考方案9】:

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

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

【讨论】:

你怎么知道在SPARK_EXECUTOR_MEMORYSPARK_DRIVER_MEMORY之间调整哪一个? 即什么错误会告诉你增加SPARK_EXECUTOR_MEMORY,什么错误会告诉你增加SPARK_DRIVER_MEMORY【参考方案10】:

我对上述错误的建议很少。

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

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

● 使用广播连接

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

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

【讨论】:

【参考方案11】:

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

【讨论】:

【参考方案12】:

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

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

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

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

【讨论】:

【参考方案13】:

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

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

【讨论】:

以上是关于Spark java.lang.OutOfMemoryError:Java 堆空间的主要内容,如果未能解决你的问题,请参考以下文章

Spark面试题——Spark资源调优

spark提交参数解析

科普Spark,Spark是啥,如何使用Spark

Spark系列

Spark-01 spark简介

Spark 内核 Spark 内核解析-下