Spark java.lang.OutOfMemoryError:Java堆空间

heq*_*128 208 out-of-memory apache-spark

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

我的设置:

spark.executor.memory=4g, Dspark.akka.frameSize=512
Run Code Online (Sandbox Code Playgroud)

这是问题所在:

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

val imageBundleRDD = sc.newAPIHadoopFile(...)
Run Code Online (Sandbox Code Playgroud)

其次,在这个RDD上做点什么:

val res = imageBundleRDD.map(data => {
                               val desPoints = threeDReconstruction(data._2, bg)
                                 (data._1, desPoints)
                             })
Run Code Online (Sandbox Code Playgroud)

最后,输出到HDFS:

res.saveAsNewAPIHadoopFile(...)
Run Code Online (Sandbox Code Playgroud)

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

.....
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
Run Code Online (Sandbox Code Playgroud)

任务太多了?

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

我怎么解决这个问题?

sam*_*est 334

我有一些建议:

  • 如果您的节点配置为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消失.更新:从火花1.6显然我们将不再需要玩这些值,火花将自动确定它们.
  • 与上面类似但是随机存储器分数.如果你的工作不需要太多的shuffle内存,那么将它设置为较低的值(这可能会导致你的shuffle溢出到磁盘,这可能会对速度造成灾难性的影响).有时当它是一个随机操作时,你需要做相反的事情,即将它设置为大的东西,比如0.8,或者确保你允许你的shuffle溢出到磁盘(这是1.0.0以来的默认值).
  • 注意内存泄漏,这些通常是由意外关闭lambda中不需要的对象引起的.诊断的方法是在日志中查找"序列化为XXX字节的任务",如果XXX大于几k或大于MB,则可能存在内存泄漏.请参阅/sf/answers/1768942031/
  • 与上述有关; 如果你确实需要大型对象,请使用广播变量.
  • 如果您正在缓存大型RDD并且可以牺牲一些访问时间,请考虑序列化RDD http://spark.apache.org/docs/latest/tuning.html#serialized-rdd-storage.或者甚至将它们缓存在磁盘上(如果使用SSD,有时也不会那么糟糕).
  • (高级)与上述相关,避免String和重度嵌套的结构(如Map嵌套的案例类).如果可能,尝试仅使用原始类型并索引所有非基元,特别是如果您期望大量重复.WrappedArray尽可能选择嵌套结构.或者甚至推出自己的序列化 - 您将获得有关如何有效地将数据备份为字节的最多信息,请使用它!
  • (有点hacky)再次在缓存时,请考虑使用a Dataset来缓存您的结构,因为它将使用更高效的序列化.与前一个要点相比,这应被视为黑客攻击.将您的领域知识构建到您的算法/序列化中可以将内存/缓存空间最小化100倍或1000倍,而所有Dataset可能的内存可能是内存中的2x - 5x和磁盘上的10x压缩(镶木地板).

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

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

java.lang.OutOfMemoryError : GC overhead limit exceeded
Run Code Online (Sandbox Code Playgroud)

  • @samthebest这是一个很棒的答案。我真的很感谢日志记录查找内存泄漏的帮助。 (2认同)
  • 您好@ user449355请问一个新问题?因为害怕开始一个长期的评论主题:)如果你遇到问题,可能是其他人,并且一个问题会让所有人更容易找到. (2认同)

Bri*_*ian 52

要向此添加一个通常没有讨论的用例,我将在本地模式下提交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 
Run Code Online (Sandbox Code Playgroud)


Tom*_*art 15

看一下在那里设置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"
Run Code Online (Sandbox Code Playgroud)

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


blu*_*kin 15

你应该增加驱动程序内存.在我的$ SPARK_HOME/conf文件夹中,您应该找到该文件spark-defaults.conf,编辑并设置spark.driver.memory 4000m取决于主服务器上的内存,我认为.这就解决了我的问题,一切顺利


小智 11

您应该配置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()
Run Code Online (Sandbox Code Playgroud)

根据您的计算机RAM可用性为驱动程序提供内存和执行程序内存.如果您仍然面临OutofMemory问题,可以增加offHeap大小.

  • 在你的代码中设置驱动内存是行不通的,请阅读spark文档:Spark属性主要可以分为两种:一种是和deploy相关的,比如“spark.driver.memory”,“spark.executor.instances”,在运行时通过 SparkConf 以编程方式设置时,此类属性可能不会受到影响,或者行为取决于您选择的集群管理器和部署模式,因此建议通过配置文件或 spark-submit 命令行选项设置。 (4认同)
  • 注意 pyspark 用户应将 `.config("spark.memory.offHeap.enabled",true)` 更改为 `.config("spark.memory.offHeap.enabled","true")`。 (4认同)
  • 最好的答案!我的问题是 Spark 没有安装在主节点上,我只是使用 PySpark 连接到 HDFS 并得到了同样的错误。使用`config`解决了这个问题。 (2认同)

Abd*_*awi 7

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

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

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

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

spark-submit --conf spark.driver.memory=18gb ....cont
Run Code Online (Sandbox Code Playgroud)

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

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

  • 您应该使用--conf spark.driver.memory = 18g (2认同)

Amn*_*non 5

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

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


小智 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辅助| 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