web-dev-qa-db-ja.com

Spark Java.lang.OutOfMemoryError:Javaヒープスペース

私のクラスタ:1マスター、11スレーブ、各ノードに6 GBのメモリがあります。

私の設定:

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

これが問題です:

最初に、HDFSからRDDにいくつかのデータ(2.19 GB)を読みます。

val imageBundleRDD = sc.newAPIHadoopFile(...)

2番目、この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であれば、すべて問題ありません。

どうすればこの問題を解決できますか?

189
hequn8128

いくつか提案があります。

  • あなたのノードがSparkのために最大6gを持つように(そして他のプロセスのために少し残っているように)設定されているなら、4gではなく6g、spark.executor.memory=6gを使います。 UIをチェックしてできるだけ多くのメモリを使用しているを確認してください(使用しているメモリの量がわかります)。
  • もっと多くのパーティションを使ってみてください。CPU当たり2 - 4が必要です。パーティションの数を増やすIMEは、プログラムをより安定させる(そしてより高速にする)ための最も簡単な方法です。大量のデータについては、CPUあたり4個以上の容量が必要な場合があります。場合によっては、8000個のパーティションを使用する必要があります。
  • spark.storage.memoryFractionを使用して、キャッシング用に予約されたメモリーの分数を減らします。コードでcache()またはpersistname__を使用しない場合、これは0になる可能性があります。デフォルトは0.6です。つまり、ヒープに使用できるメモリはわずか0.4 * 4gです。 IMEがメモリの枠を減らすと、しばしばOOMがなくなります。 PDATE: spark 1.6から、どうやらこれらの値で遊ぶ必要はもうないようです、sparkはそれらを自動的に決定します。
  • 上記と似ていますが、シャッフルメモリの割合です。あなたの仕事があまりシャッフルメモリを必要としないなら、それをより低い値に設定してください(これはあなたのシャッフルがスピードに壊滅的な影響を与えることができるディスクにあふれさせるかもしれません)。それがOOMingのシャッフル操作であるとき、時々あなたは反対のことをする必要がありますすなわち0.8のような何かにそれを設定するか、あなたがあなたのシャッフルがディスクにあふれることを可能にすることを確かめます(1.0.0以来のデフォルトです)。
  • メモリリークに注意してください。これらは、ラムダに不要なオブジェクトを誤って閉じてしまうことが原因であることがよくあります。診断する方法は、ログ内の「XXXバイトとしてシリアライズされたタスク」を探すことです。XXXが数kより大きい場合、または1 M​​Bを超える場合、メモリリークが発生する可能性があります。 https://stackoverflow.com/a/25270600/1586965 を参照してください。
  • 上記に関連します。ラージオブジェクトが本当に必要な場合は、ブロードキャスト変数を使用してください。
  • 大規模なRDDをキャッシュしていてアクセス時間を犠牲にすることができる場合は、RDDを直列化することを検討してください http://spark.Apache.org/docs/latest/tuning.html#serialized-rdd-storage あるいは、それらをディスクにキャッシュすることさえあります(SSDを使用している場合は、それほど悪いことではありません)。
  • Advanced)上記に関連して、Stringname__およびネスト構造の重いもの(Mapname__およびネストしたケースクラスなど)は避けてください。可能であれば、プリミティブ型のみを使用し、すべての非プリミティブにインデックスを付けるようにしてください。特に、多数の重複が予想される場合にはそうします。可能であれば、ネストされた構造体に対してWrappedArrayname__を選択してください。あるいは、あなた自身のシリアライゼーションを展開することさえできます - あなたはあなたのデータをバイトに効率的にバックアップする方法に関するほとんどの情報を持つでしょう、SE IT
  • bit hacky)再度キャッシュするときは、Datasetname__を使用して構造をキャッシュすることを検討してください。より効率的なシリアル化が使用されるためです。前の箇条書きと比較すると、これはハックと見なされるべきです。ドメインの知識をalgo/serializationに組み込むことで、メモリ/キャッシュスペースを100倍または1000倍に減らすことができます。一方、Datasetname__は、メモリで2倍から5倍、ディスクで10倍の圧縮(寄木細工)です。

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

編集:(だから私は自分自身を簡単にグーグルすることができます)次もこの問題の指標である:

Java.lang.OutOfMemoryError : GC overhead limit exceeded
310
samthebest

これには議論されないことが多いユースケースを追加するために、localモードでspark-submitを介してSparkアプリケーションを送信するときの解決策を提示します。

Gitbookによると Apache SparkJacek Laskowski でマスターしています。

Sparkをローカルモードで実行することができます。この非分散シングルJVMデプロイメントモードでは、Sparkはすべての実行コンポーネント(ドライバ、エグゼキュータ、バックエンド、およびマスタ)を同じJVM内に生成します。これは、ドライバが実行に使用される唯一のモードです。

したがって、OOMheapエラーが発生した場合は、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 
48
Brian Vanover

ドライバメモリを増やしてください。あなたの$ SPARK_HOME/confフォルダの中にはspark-defaults.confというファイルがあるはずです。マスターのメモリに応じてspark.driver.memory 4000mを編集して設定してください。これは私にとって問題を修正したものであり、すべてがスムーズに実行されます

14
blueskin

起動スクリプト に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"

スクリプトをデプロイするためのドキュメントは、 here にあります。

14
Tombart

以下に示すように、オフヒープメモリ設定を構成する必要があります。

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サイズを増やすことができます

7
pavan.vn101

大まかに言って、spark Executor JVMメモリは2つの部分に分けられます。スパークメモリとユーザメモリこれは、プロパティspark.memory.fractionによって制御されます。値は0から1の間です。スパークアプリケーションでイメージを処理したり、メモリを多用する処理を行う場合は、spark.memory.fractionを減らすことを検討してください。これにより、アプリケーションの作業に使用できるメモリが増えます。 Sparkはこぼれる可能性があるので、それでも少ないメモリ共有で動作します。

問題の2番目の部分は仕事の分割です。可能であれば、データをより小さな部分に分割してください。データが小さいほど、メモリが少なくて済みます。しかし、それが不可能な場合は、メモリのために計算を犠牲にします。通常、単一のexecutorが複数のコアを実行します。エグゼキュータの総メモリは、すべての並行タスクのメモリ要件を処理するのに十分でなければなりません。エクゼキュータのメモリを増やすことが選択できない場合は、エクゼキュータごとのコアを減らして、各タスクがより多くのメモリを使えるようにすることができます。あなたが与えることができる最大のメモリを持っているそしてあなたが最高のコアカウントを見つけるまでコアを増やし続ける1コアエクゼキュータでテストしなさい。

4
Rohit Karlupia

私はこの問題に悩まされています。動的リソース割り当てを使用していますが、クラスターリソースを利用してアプリケーションに最適になると思いました。

しかし、実のところ、動的なリソース割り当てはドライバのメモリを設定せず、デフォルト値の1gに維持します。

Spark.driver.memoryをドライバのメモリに合った数に設定して解決しました(32GB RAMの場合は18GBに設定しました)。

次のようにspark submitコマンドを使用して設定できます。

spark-submit --conf spark.driver.memory=18gb ....cont

非常に重要な注意、あなたがコードからそれを設定した場合、このプロパティは考慮に入れられないでしょう。

Sparkのプロパティは主に2種類に分けられます。1つは「spark.driver.memory」、「spark.executor.instances」のようにデプロイに関連するもので、ランタイムでSparkConfを介してプログラムで設定する場合、この種類のプロパティは影響を受けません。動作は選択したクラスタマネージャとデプロイモードによって異なります。そのため、設定ファイルまたはspark-submitコマンドラインオプションを使用して設定することをお勧めします。もう1つは、 "spark.task.maxFailures"のように主にSparkランタイム制御に関連しています。この種のプロパティはどちらの方法でも設定できます。

3

メモリヒープサイズを設定する場所(少なくともspark-1.0.0)はconf/spark-envにあります。関連する変数はSPARK_EXECUTOR_MEMORYSPARK_DRIVER_MEMORYです。その他のドキュメントは デプロイメントガイドにあります

また、設定ファイルをすべてのスレーブノードにコピーすることを忘れないでください。

2
Amnon

マスター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

2
Yunzhao Yang

私は上記のエラーについていくつかの提案があります。

●エクゼキュータとして割り当てられたエクゼキュータメモリをチェックすると、割り当てられたものより多くのメモリを必要とするパーティションを処理する必要があります。

●シャッフルはディスクI/O、データのシリアル化、およびネットワークI/Oを伴うため、シャッフルは高価な操作であるため、より多くのシャッフルが稼働しているかどうかを確認します

●ブロードキャスト参加を使用する

●groupByKeysの使用を避け、ReduceByKeyに置き換えてみる

●シャッフルが発生する場所では、巨大なJavaオブジェクトを使用しないでください。

1