我有一个火花工作,在第0阶段有数十万(300,000任务和更多)任务,然后在洗牌期间,驱动程序端会抛出以下异常:
util.Utils: Suppressing exception in finally: null
java.lang.OutOfMemoryError at
java.io.ByteArrayOutputStream.hugeCapacity(ByteArrayOutputStream.java:123) at
java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:117) at
java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93) at
java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153) at
java.util.zip.DeflaterOutputStream.deflate(DeflaterOutputStream.java:253) at
java.util.zip.DeflaterOutputStream.write(DeflaterOutputStream.java:211) at
java.util.zip.GZIPOutputStream.write(GZIPOutputStream.java:145) at
java.io.ObjectOutputStream$BlockDataOutputStream.writeBlockHeader(ObjectOutputStream.java:1894) at
java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1875) at
java.io.ObjectOutputStream$BlockDataOutputStream.flush(ObjectOutputStream.java:1822) at
java.io.ObjectOutputStream.flush(ObjectOutputStream.java:719) at
java.io.ObjectOutputStream.close(ObjectOutputStream.java:740) at
org.apache.spark.MapOutputTracker$$anonfun$serializeMapStatuses$2.apply$mcV$sp(MapOutputTracker.scala:618) at
org.apache.spark.util.Utils$.tryWithSafeFinally(Utils.scala:1319) at
org.apache.spark.MapOutputTracker$.serializeMapStatuses(MapOutputTracker.scala:617) at
org.apache.spark.MapOutputTrackerMaster.getSerializedMapOutputStatuses(MapOutputTracker.scala:560) at
org.apache.spark.MapOutputTrackerMaster$MessageLoop.run(MapOutputTracker.scala:349) at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745)
我检查了ByteArrayOutputStream代码,当数组大小大于INTEGER.MAX(大约2G)时,它会抛出内存不足错误。这意味着地图状态序列化结果应小于2G。
我还检查了MapOutputTracker代码,这个地图状态大小与任务大小和后续阶段任务大小有关。
我想知道是否有人遇到这个问题,你是如何解决这个问题的。我的理解是我们只能减少任务的大小,但我的任务只能停留,因为较少的分区会延迟计算。
答案 0 :(得分:0)
这可能是由于一个shuffle中超过2GB内存的单个块造成的。这通常意味着您的操作需要更大的并行性,这将减小任何单个块的大小 - 希望低于2GB限制(这是非常高的。)
没有Spark shuffle块可以大于2 GB
Spark使用ByteBuffer作为存储块的抽象。
val buf =ByteBuffer.allocate(length.toInt)
ByteBuffer is limited by Integer.MAX_IZE(2GB)
提高并行度
1)在调用导致此错误的操作之前重新分区数据,如下所示:
DataFrame.repartition(400)
RDD.repartition(400)
2)将分区数作为最后一个参数(支持的位置)传递给操作:
import org.apache.spark.rdd.PairRDDFunctions
RDD.groupByKey(numPartitions: Int) RDD.join(other: RDD, numPartitions:Int)
3)通过SparkConf设置默认并行度(分区),如下所示(在Databricks Cloud中不支持):
// create the SparkConf used to create the SparkContext val conf = new SparkConf()
// set the parallelism/partitions conf.set("spark.default.parallelism", 400)
// create the SparkContext with the conf val sc = new SparkContext(conf)
// check the parallelism/partitions sc.defaultParallelism
4)通过SQL设置SQL分区,如下所示(默认为200):
SET spark.sql.shuffle.partitions = 400;
为什么2GB?
由于Java Integers的限制,存在此限制:2 ^ 31 == 2,147,483,647~ = 2GB。
Spark的shuffle机制目前使用Java ByteArrays通过网络传输数据。
将来通过扩展Spark的shuffle来使用Longs使用更大的ByteArrays,将ByteArrays链接在一起,或者两者兼而有之,这将会得到增强。
https://forums.databricks.com/questions/1140/im-seeing-an-outofmemoryerror-requested-array-size.html