Spark java.lang.OutOfMemoryError:Java堆空间


228

我的集群: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时,一切正常。

我怎么解决这个问题?


如何运行火花?是从控制台吗?或您使用哪个部署脚本?
Tombart 2014年

我使用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大得多。谢谢〜
hequn8128

您提到的三个步骤是您唯一要做的吗?(data._1,desPoints)生成的数据量的大小是多少-如果将该数据改组到另一个阶段,则该数据量应该适合内存esp
Arnon Rotem-Gal-Oz

1
驱动程序的内存配置是什么?检查哪个服务器出现内存不足错误。是驱动程序还是执行程序之一。
RanP

此处查看所有配置属性:spark.apache.org/docs/2.1.0/configuration.html
Naramsim

Answers:


363

我有一些建议:

  • 如果您的节点配置为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减少内存碎片通常会使OOM消失。更新:从spark 1.6开始,显然我们将不再需要使用这些值,spark会自动确定它们。
  • 与上面相似,但改写了记忆分数。如果您的工作不需要太多的随机存取存储器,则将其设置为较低的值(这可能会导致随机存取存储器溢出到磁盘上,从而可能对速度造成灾难性的影响)。有时,当它是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尽可能选择嵌套结构。甚至推出自己的序列化-您将获得有关如何有效地将数据有效地备份为字节的更多信息,请使用它
  • 有点hacky)再次进行缓存时,请考虑使用a 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,以确保工作程序已注册并具有足够的内存”。将spark.storage.memoryFraction设置为0.1也无法解决问题。也许问题出在我的代码中,谢谢!
hequn8128'4

2
@samthebest这是一个很棒的答案。我真的很感谢日志记录查找内存泄漏的帮助。
Myles Baker

1
嗨,@ samthebest,您是如何指定8000个分区的?由于我使用的是Spark sql,因此只能使用spark.sql.shuffle.partitions指定分区,默认值是200,如果我将其设置为更多,我尝试将其设置为1000,但没有帮助获得OOM,您知道什么是最佳选择分区值我有1 TB的倾斜数据要处理,并且涉及按配置单元查询。请指导。
Umesh K

2
嗨@ user449355,您能问一个新问题吗?担心开始冗长的注释线程:)如果您遇到问题,很可能其他人也遇到了问题,这样一个问题将使所有人都更容易找到。
samthebest,2015年

1
首先,@ samthebest不应使用所有内存,spark.executor.memory因为您肯定需要一定数量的内存用于I / O开销。如果您全部使用它,它将减慢您的程序速度。Unix可能是例外,在这种情况下,您具有交换空间。
Hunle

58

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

按照gitbook 掌握阿帕奇星火亚采郭先生

您可以在本地模式下运行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 

在独立模式下,应该考虑多少百分比的驱动程序内存。
Yashwanth Kambala,

@Brian,在本地模式下,驱动程序内存是否需要大于输入数据大小?是否可以为输入数据集指定分区数,以便Spark作业可以处理比可用RAM大得多的数据集?
fuyi

19

您应该配置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设置帮助
kennyut

2
在代码中设置驱动程序内存将不起作用,为此请阅读spark文档:Spark属性主要可以分为两种:一种与部署相关,例如“ spark.driver.memory”,“ spark.executor.instances”,在运行时通过SparkConf进行编程设置时,此类属性可能不会受到影响,或者行为取决于您选择的集群管理器和部署模式,因此建议您通过配置文件或spark-submit命令行选项进行设置。
Abdulhafeth Sartawi,

1
最好的答案!我的问题是Spark没有安装在主节点上,我只是使用PySpark连接到HDFS并遇到了同样的错误。使用config解决了问题。
Mikhail_Sam

我刚刚使用spark-submit命令添加了配置,以解决堆大小问题。谢谢。
Pritam Sadhukhan

16

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


MEM的多少百分比获分配,在单机
Yashwanth Kambala

14

看一下在其中设置了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"

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


谢谢〜我稍后再试。从spark ui,它显示每个执行程序的内存为4096。因此已启用该设置,对吗?
hequn8128 2014年

当我遇到类似的问题时,您已经看到了答案(stackoverflow.com/questions/34762432/…)。看您提供的链接似乎不再设置Xms / Xmx,您能说出原因吗?
塞菲,2016年

start up scripts不幸的是,链接到的脚本上的内容已更改。截至2019
-David Groomes

7

我在这个问题上受了很多苦,我们使用动态资源分配,我认为它将利用我的群集资源来最适合该应用程序。

但事实是,动态资源分配不会设置驱动程序内存,而是将其保留为默认值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”,可以用任何一种方式设置这种属性。


2
您应该使用--conf spark.driver.memory = 18g
merenptah

5

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

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


5

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


3

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

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


4
您如何知道在SPARK_EXECUTOR_MEMORY&之间调整哪一个SPARK_DRIVER_MEMORY
Hunle

13
即是什么错误会告诉你增加SPARK_EXECUTOR_MEMORY,而什么错误会告诉你增加SPARK_DRIVER_MEMORY
Hunle '16

2

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

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

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

●使用广播联接

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

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


抱歉劫持了别人的查询,但是如何在groupBy上使用reduceByKey?
索米尔·阿塞亚

1

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

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

0

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

spark-submit --conf spark.yarn.maxAppAttempts=2 --executor-memory 10g --num-executors 50 --driver-memory 12g
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.