Spark java.lang.OutOfMemoryError: spazio heap Java


228

Il mio cluster: 1 master, 11 slave, ogni nodo ha 6 GB di memoria.

Le mie impostazioni:

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

Ecco il problema:

Innanzitutto , ho letto alcuni dati (2,19 GB) da HDFS a RDD:

val imageBundleRDD = sc.newAPIHadoopFile(...)

Secondo , fai qualcosa su questo RDD:

val res = imageBundleRDD.map(data => {
                               val desPoints = threeDReconstruction(data._2, bg)
                                 (data._1, desPoints)
                             })

Infine , output su HDFS:

res.saveAsNewAPIHadoopFile(...)

Quando eseguo il mio programma mostra:

.....
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

Ci sono troppi compiti?

PS : tutto è ok quando i dati di input sono di circa 225 MB.

Come posso risolvere questo problema?


come si esegue la scintilla? è dalla console? o quali script di distribuzione usi?
Tombart,

Uso sbt per compilare ed eseguire la mia app. pacchetto sbt quindi sbt in esecuzione. Ho implementato lo stesso programma su hadoop un mese fa e ho riscontrato lo stesso problema di OutOfMemoryError, ma in hadoop può essere facilmente risolto aumentando il valore di mapred.child.java.opts da Xmx200m a Xmx400m. Spark ha qualche impostazione jvm per i suoi compiti? Mi chiedo se spark.executor.memory abbia lo stesso significato di mapred.child.java.opts in hadoop. Nel mio programma spark.executor.memory è già stato impostato su 4g molto più grande di Xmx400m in hadoop. Grazie ~
hequn8128,

I tre passaggi che menzioni sono gli unici che fai? Qual è la dimensione dei dati generati da (data._1, desPoints) - questo dovrebbe adattarsi alla memoria specialmente se questi dati vengono poi rimescolati in un altro stadio
Arnon Rotem-Gal-Oz,

1
Qual è la configurazione della memoria per il driver? Controlla quale server ha esaurito l'errore di memoria. È il driver o uno degli esecutori.
RanP

Vedi qui tutte le proprietà delle configurazioni: spark.apache.org/docs/2.1.0/configuration.html
Naramsim

Risposte:


364

Ho alcuni suggerimenti:

  • Se i nodi sono configurati per avere la massima 6g per la Spark (e sono lasciando un po 'per altri processi), quindi utilizzare 6g piuttosto che 4g, spark.executor.memory=6g. Assicurati di utilizzare quanta più memoria possibile controllando l'interfaccia utente (indicherà la quantità di mem che stai utilizzando)
  • Prova a usare più partizioni, dovresti avere 2-4 per CPU. L'IME che aumenta il numero di partizioni è spesso il modo più semplice per rendere un programma più stabile (e spesso più veloce). Per enormi quantità di dati potresti aver bisogno di più di 4 per CPU, in alcuni casi ho dovuto usare 8000 partizioni!
  • Ridurre la frazione di memoria riservata per la memorizzazione nella cache , utilizzando spark.storage.memoryFraction. Se non usi cache()o persistnel tuo codice, questo potrebbe anche essere 0. Il suo valore predefinito è 0,6, il che significa che hai solo 0,4 * 4 g di memoria per il tuo heap. L'IME che riduce il mem frac spesso fa scomparire le OOM. AGGIORNAMENTO: da spark 1.6 apparentemente non avremo più bisogno di giocare con questi valori, spark li determinerà automaticamente.
  • Simile alla frazione di memoria precedente ma casuale . Se il tuo lavoro non ha bisogno di molta memoria shuffle, impostalo su un valore inferiore (questo potrebbe far sì che i tuoi shuffles si riversino su disco, il che può avere un impatto catastrofico sulla velocità). A volte, quando si tratta di un'operazione shuffle che sta per OOM, devi fare il contrario, ovvero impostarlo su qualcosa di grande, come 0,8, o assicurarti di consentire che i tuoi shuffles si riversino sul disco (è l'impostazione predefinita dalla 1.0.0).
  • Fai attenzione alle perdite di memoria , che sono spesso causate dalla chiusura accidentale di oggetti non necessari nei lambda. Il modo per diagnosticare è cercare "l'attività serializzata come XXX byte" nei registri, se XXX è più grande di qualche k o più di un MB, si potrebbe avere una perdita di memoria. Vedi https://stackoverflow.com/a/25270600/1586965
  • Relativo a sopra; usa le variabili di trasmissione se hai davvero bisogno di oggetti di grandi dimensioni.
  • Se stai memorizzando nella cache grandi RDD e puoi sacrificare un po 'di tempo di accesso, considera la serializzazione del RDD http://spark.apache.org/docs/latest/tuning.html#serialized-rdd-storage . O anche memorizzarli nella cache su disco (che a volte non è poi così male se si usano SSD).
  • ( Avanzato ) Relativo a sopra, evitare Stringe strutture nidificate pesantemente ( Mapclassi di case simili e nidificate). Se possibile, prova a utilizzare solo i tipi primitivi e indicizza tutti i non primitivi, soprattutto se ti aspetti molti duplicati. Scegli WrappedArraytra strutture nidificate quando possibile. O addirittura implementare la propria serializzazione: avrai la maggior parte delle informazioni su come eseguire il backup dei dati in byte in modo efficiente, USARLO !
  • ( bit hacky ) Ancora una volta durante la memorizzazione nella cache, considerare l'utilizzo di a Datasetper memorizzare nella cache la struttura in quanto utilizzerà una serializzazione più efficiente. Questo dovrebbe essere considerato un trucco rispetto al precedente punto elenco. Incorporare le tue conoscenze di dominio nel tuo algo / serializzazione può ridurre al minimo lo spazio di memoria / cache di 100x o 1000x, mentre tutto ciò che una Datasetvolontà fornirà probabilmente è 2x - 5x in memoria e 10x compresso (parquet) su disco.

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

EDIT: (Quindi posso google me stesso più facilmente) Quanto segue è anche indicativo di questo problema:

java.lang.OutOfMemoryError : GC overhead limit exceeded

Grazie per i tuoi suggerimenti ~ Se imposto spark.executor.memory = 6g, spark avrà il problema: "controlla l'interfaccia utente del tuo cluster per assicurarti che i lavoratori siano registrati e abbiano memoria sufficiente". L'impostazione di spark.storage.memoryFraction su 0.1 non può risolvere il problema. Forse il problema risiede nel mio codice. Grazie!
hequn8128,

2
@samthebest Questa è una risposta fantastica. Apprezzo molto l'aiuto di registrazione per trovare perdite di memoria.
Myles Baker,

1
Ciao @samthebest come hai specificato 8000 partizioni? Dal momento che sto usando Spark sql posso solo specificare la partizione usando spark.sql.shuffle.partitions, il valore predefinito è 200 dovrei impostarlo su più Ho provato a impostarlo su 1000 ma non aiutando a ottenere OOM sei consapevole di quale dovrebbe essere l'ottimale valore di partizione Ho elaborato 1 TB di dati distorti da elaborare e coinvolgono query raggruppate per alveare. Per favore guida.
Umesh K,

2
Ciao @ user449355 per favore potresti fare una nuova domanda? Per paura di iniziare un lungo thread di commenti :) Se stai riscontrando problemi, probabilmente lo sono altre persone e una domanda faciliterebbe la ricerca di tutti.
Samthebest

1
Per il tuo primo punto, @samthebest, non dovresti usare TUTTA la memoria spark.executor.memoryperché sicuramente hai bisogno di una certa quantità di memoria per l'overhead I / O. Se lo usi tutto, rallenterà il tuo programma. L'eccezione a ciò potrebbe essere Unix, nel qual caso hai spazio di swap.
Hunle,

58

Per aggiungere un caso d'uso a questo che spesso non viene discusso, proporrò una soluzione quando inoltrerò una Sparkdomanda tramite spark-submitin modalità locale .

Secondo il gitbook Mastering Apache Spark di Jacek Laskowski :

È possibile eseguire Spark in modalità locale. In questa modalità di distribuzione JVM singola non distribuita, Spark genera tutti i componenti di esecuzione (driver, esecutore, back-end e master) nella stessa JVM. Questa è l'unica modalità in cui un driver viene utilizzato per l'esecuzione.

Quindi, se stai vivendo OOM errori con heap, è sufficiente regolare driver-memoryanziché anziché executor-memory.

Ecco un esempio:

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 

Quanta percentuale dovremmo considerare per la memoria del driver in modalità autonoma.
Yashwanth Kambala,

@Brian, in modalità locale, la memoria del driver deve essere maggiore della dimensione dei dati di input? È possibile specificare il numero di partizioni per il set di dati di input, in modo che il processo Spark possa gestire set di dati molto più grandi della RAM disponibile?
fuyi,

19

È necessario configurare offHeap memory settings come mostrato di seguito:

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()

Fornisci la memoria del driver e quella dell'esecutore secondo la disponibilità della RAM della tua macchina. Puoi aumentare le dimensioni di OffHeap se stai ancora affrontando il problema di OutofMemory .


Aggiunta l'impostazione dell'heap ha aiutato
kennyut il

2
l'impostazione della memoria del driver nel tuo codice non funzionerà, leggi la documentazione di spark per questo: le proprietà di Spark possono principalmente essere divise in due tipi: uno è correlato alla distribuzione, come "spark.driver.memory", "spark.executor.instances", questo tipo di proprietà potrebbe non essere influenzato dall'impostazione a livello di programmazione tramite SparkConf in fase di esecuzione, oppure il comportamento dipende dal gestore cluster e dalla modalità di distribuzione scelta, pertanto si consiglia di impostare tramite il file di configurazione o le opzioni della riga di comando spark-submit.
Abdulhafeth Sartawi,

1
LA MIGLIORE RISPOSTA! Il mio problema era che Spark non era installato sul nodo master, ho appena usato PySpark per connettermi a HDFS e ho avuto lo stesso errore. Utilizzando configrisolto il problema.
Mikhail_Sam,

Ho appena aggiunto le configurazioni usando il comando spark-submit per risolvere il problema relativo alle dimensioni dell'heap. Grazie.
Pritam Sadhukhan,

16

È necessario aumentare la memoria del driver. Nella tua cartella $ SPARK_HOME / conf dovresti trovare il file spark-defaults.conf, modificarlo e impostarlo in spark.driver.memory 4000mbase alla memoria del tuo master, credo. Questo è ciò che ha risolto il problema per me e tutto funziona senza intoppi


Quanta percentuale di mem da assegnare, in stand alone
Yashwanth Kambala

14

Dai un'occhiata agli script di avvio in cui è impostata una dimensione heap Java, sembra che tu non lo stia impostando prima di eseguire 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"

Puoi trovare la documentazione per distribuire gli script qui .


Grazie ~ ci proverò più tardi. Da spark ui, mostra che la memoria di ogni esecutore è 4096. Quindi l'impostazione è stata abilitata, giusto?
hequn8128,

Ho visto la tua risposta mentre sto affrontando un problema simile ( stackoverflow.com/questions/34762432/… ). Guardando il link che hai fornito sembra che l'impostazione di Xms / Xmx non sia più presente, puoi dire perché?
Seffy,

start up scriptsSfortunatamente, il contenuto dello script collegato da è cambiato. Nessuna di queste opzioni esiste dal 19-12-2019
David Groomes,

7

Ho sofferto molto di questo problema, utilizziamo l'allocazione dinamica delle risorse e ho pensato che utilizzerà le risorse del mio cluster per adattarsi al meglio all'applicazione.

Ma la verità è che l'allocazione dinamica delle risorse non imposta la memoria del driver e la mantiene al suo valore predefinito che è 1g.

L'ho risolto impostando spark.driver.memory su un numero adatto alla memoria del mio driver (per 32 GB di RAM l'ho impostato su 18 GB)

puoi impostarlo usando il comando spark submit come segue:

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

Nota molto importante, questa proprietà non verrà presa in considerazione se la si imposta dal codice, secondo la documentazione spark:

Le proprietà di Spark possono essere principalmente suddivise in due tipi: uno è correlato alla distribuzione, come "spark.driver.memory", "spark.executor.instances", questo tipo di proprietà potrebbe non essere influenzato durante l'impostazione a livello di programmazione tramite SparkConf in fase di esecuzione, oppure il comportamento dipende dal gestore cluster e dalla modalità di distribuzione scelta, quindi si consiglia di impostare le opzioni della riga di comando tramite il file di configurazione o spark-submit; un altro è principalmente legato al controllo di runtime Spark, come "spark.task.maxFailures", questo tipo di proprietà può essere impostato in entrambi i modi.


2
Dovresti usare --conf spark.driver.memory = 18g
merenptah

5

In linea di massima, la memoria Spark Executor JVM può essere divisa in due parti. Scintilla memoria e memoria utente. Questo è controllato dalla proprietà spark.memory.fraction: il valore è compreso tra 0 e 1. Quando si lavora con le immagini o si esegue un'elaborazione intensiva della memoria in applicazioni spark, si consiglia di ridurrespark.memory.fraction . Ciò renderà disponibile più memoria per l'applicazione. Spark può fuoriuscire, quindi funzionerà comunque con meno condivisione di memoria.

La seconda parte del problema è la divisione del lavoro. Se possibile, suddividere i dati in blocchi più piccoli. Dati più piccoli potrebbero richiedere meno memoria. Ma se ciò non è possibile, sei un sacrificio di calcolo per la memoria. In genere un singolo esecutore eseguirà più core. La memoria totale degli esecutori deve essere sufficiente per gestire i requisiti di memoria di tutte le attività simultanee. Se aumentare la memoria dell'esecutore non è un'opzione, è possibile ridurre i core per esecutore in modo che ogni attività ottenga più memoria con cui lavorare. Metti alla prova con 1 core esecutori che hanno la memoria più grande che puoi dare e quindi continua ad aumentare i core fino a trovare il miglior numero di core.


5

Hai scaricato il tuo registro principale di GC? Quindi ho riscontrato un problema simile e ho trovato SPARK_DRIVER_MEMORY impostare solo l'heap Xmx. La dimensione iniziale dell'heap rimane 1G e la dimensione dell'heap non viene mai ridimensionata rispetto all'heap Xmx.

Il passaggio "--conf" spark.driver.extraJavaOptions = -Xms20g "risolve il problema.

ps aux | grep java e vedrai il seguente registro: =

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

La posizione per impostare la dimensione dell'heap di memoria (almeno in spark-1.0.0) è in conf / spark-env. Le variabili rilevanti sono SPARK_EXECUTOR_MEMORY& SPARK_DRIVER_MEMORY. Altri documenti sono nella guida alla distribuzione

Inoltre, non dimenticare di copiare il file di configurazione su tutti i nodi slave.


4
Come fai a sapere quale regolare tra SPARK_EXECUTOR_MEMORY& SPARK_DRIVER_MEMORY?
Hunle,

13
cioè quale errore ti direbbe di aumentare SPARK_EXECUTOR_MEMORYe quale errore ti direbbe di aumentare SPARK_DRIVER_MEMORY?
Hunle,

2

Ho alcuni suggerimenti per l'errore sopra menzionato.

● Controllare che la memoria dell'esecutore assegnata come esecutore potrebbe dover gestire partizioni che richiedono più memoria di quella assegnata.

● Provare a vedere se sono attivi più shuffle poiché i shuffle sono operazioni costose poiché coinvolgono I / O su disco, serializzazione dei dati e I / O di rete

● Usa Broadcast Join

● Evitare di utilizzare groupByKeys e provare a sostituire con ReduceByKey

● Evitare di utilizzare enormi oggetti Java ovunque si verifichino shuffle


Mi dispiace dirottare la query di qualcun altro, ma come utilizzare riduci il tasto sul gruppo?
Somil Aseeja,

1

Dalla mia comprensione del codice fornito sopra, carica il file, esegue il mapping e lo salva. Non ci sono operazioni che richiedono shuffle. Inoltre, non vi è alcuna operazione che richieda che i dati vengano portati al driver, pertanto la messa a punto di qualsiasi cosa relativa a shuffle o driver potrebbe non avere alcun impatto. Il driver ha problemi quando ci sono troppe attività ma questo è stato solo fino alla versione spark 2.0.2. Ci possono essere due cose che vanno male.

  • Ci sono solo uno o pochi esecutori. Aumentare il numero di esecutori in modo che possano essere assegnati a diversi slave. Se stai usando il thread devi cambiare la configurazione di num-execors o se stai usando spark standalone allora devi mettere a punto num core per esecutore e spark max core conf. In esecutori num standalone = numero massimo di core / core per esecutore.
  • Il numero di partizioni è molto limitato o forse solo uno. Quindi, se questo è basso anche se abbiamo multi-core, multi-esecutori non sarà di grande aiuto poiché la parallelizzazione dipende dal numero di partizioni. Quindi aumentare le partizioni facendo imageBundleRDD.repartition (11)

0

L'impostazione di queste configurazioni esatte ha aiutato a risolvere il problema.

spark-submit --conf spark.yarn.maxAppAttempts=2 --executor-memory 10g --num-executors 50 --driver-memory 12g
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.