Spark java.lang.OutOfMemoryError: espaço de heap Java


228

Meu cluster: 1 mestre, 11 escravos, cada nó tem 6 GB de memória.

Minhas configurações:

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

Aqui está o problema:

Primeiro , li alguns dados (2,19 GB) do HDFS para o RDD:

val imageBundleRDD = sc.newAPIHadoopFile(...)

Segundo , faça algo neste RDD:

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

Por último , saída para HDFS:

res.saveAsNewAPIHadoopFile(...)

Quando executo meu programa, ele 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

Existem muitas tarefas?

PS : Tudo está bem quando os dados de entrada têm cerca de 225 MB.

Como posso resolver este problema?


como executar faísca? é do console? ou quais scripts de implantação você usa?
Tombart 15/01/14

Eu uso o sbt para compilar e executar meu aplicativo. pacote sbt e então sbt executado. Eu implementei o mesmo programa no hadoop há um mês e encontrei o mesmo problema do OutOfMemoryError, mas no hadoop ele pode ser facilmente resolvido aumentando o valor de mapred.child.java.opts de Xmx200m para Xmx400m. O spark tem alguma configuração de jvm para suas tarefas? Gostaria de saber se spark.executor.memory tem o mesmo significado que mapred.child.java.opts no hadoop. No meu programa, spark.executor.memory já foi configurado para 4g muito maior que o Xmx400m no hadoop. Obrigado ~
hequn8128

Os três passos mencionados são os únicos que você faz? Qual é o tamanho do dataa gerada por (data._1, desPoints) - isto deve caber na memória esp se esses dados são, em seguida, arrastou a outra etapa
Arnon Rotem-Gal-Oz

1
Qual é a configuração de memória para o driver? Verifique qual servidor obtém o erro de falta de memória. É o driver ou um dos executores.
RanP

Veja aqui todas as propriedades de configuração: spark.apache.org/docs/2.1.0/configuration.html
Naramsim 16/17

Respostas:


364

Tenho algumas sugestões:

  • Se seus nós estiverem configurados para ter 6g no máximo para o Spark (e estiverem deixando um pouco para outros processos), use 6g em vez de 4g spark.executor.memory=6g,. Verifique se você está usando o máximo de memória possível , verificando a interface do usuário (ele indica quanto mem você está usando)
  • Tente usar mais partições, você deve ter 2 - 4 por CPU. O IME aumentando o número de partições é geralmente a maneira mais fácil de tornar um programa mais estável (e geralmente mais rápido). Para grandes quantidades de dados, você pode precisar de mais de 4 por CPU, eu tive que usar 8000 partições em alguns casos!
  • Diminua a fração de memória reservada para armazenamento em cache usando spark.storage.memoryFraction. Se você não usar cache()ou persistno seu código, pode ser zero. O padrão é 0,6, o que significa que você recebe apenas 0,4 * 4g de memória para sua pilha. O IME que reduz o mem frac geralmente faz com que os OOMs desapareçam. ATUALIZAÇÃO: A partir do spark 1.6, aparentemente, não precisamos mais jogar com esses valores, o spark os determinará automaticamente.
  • Semelhante à acima, mas embaralhe a fração da memória . Se o seu trabalho não precisar de muita memória aleatória, defina-o como um valor mais baixo (isso pode fazer com que as aleatórias se espalhem para o disco, o que pode ter um impacto catastrófico na velocidade). Às vezes, quando uma operação de reprodução aleatória ocorre, você precisa fazer o oposto, ou seja, configurá-la para algo grande, como 0,8, ou certifique-se de permitir que suas reproduções sejam derramadas no disco (é o padrão desde a versão 1.0.0).
  • Cuidado com vazamentos de memória , geralmente causados ​​pelo fechamento acidental de objetos que você não precisa em suas lambdas. A maneira de diagnosticar é procurar a "tarefa serializada como XXX bytes" nos logs; se XXX for maior que alguns k ou mais que um MB, você poderá ter um vazamento de memória. Consulte https://stackoverflow.com/a/25270600/1586965
  • Relacionado a acima; use variáveis ​​de transmissão se você realmente precisar de objetos grandes.
  • Se você estiver armazenando em cache grandes RDDs e puder sacrificar algum tempo de acesso, considere serializar o RDD http://spark.apache.org/docs/latest/tuning.html#serialized-rdd-storage . Ou até mesmo armazená-las em cache no disco (que às vezes não é tão ruim se você estiver usando SSDs).
  • ( Avançado ) Relacionado ao acima, evite Stringe estruturas muito aninhadas ( Mapclasses de casos semelhantes e aninhadas). Se possível, tente usar apenas tipos primitivos e indexar todos os não primitivos, especialmente se você espera muitas duplicatas. Escolha WrappedArrayestruturas aninhadas sempre que possível. Ou até mesmo implante sua própria serialização - você terá mais informações sobre como fazer backup eficiente de seus dados em bytes, USE-O !
  • ( pouco hacky ) Novamente ao fazer o cache, considere usar a Datasetpara armazenar em cache sua estrutura, pois ela usará uma serialização mais eficiente. Isso deve ser considerado um hack quando comparado ao ponto anterior. A criação do conhecimento do seu domínio no seu algo / serialização pode minimizar o espaço da memória / cache em 100x ou 1000x, enquanto tudo o Datasetque provavelmente será fornecido é de 2x a 5x na memória e 10x compactado (parquet) no disco.

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

EDIT: (Para que eu possa me pesquisar no Google mais facilmente) O seguinte também é indicativo desse problema:

java.lang.OutOfMemoryError : GC overhead limit exceeded

Obrigado por suas sugestões ~ Se eu definir spark.executor.memory = 6g, o spark terá o problema: "verifique a interface do usuário do cluster para garantir que os trabalhadores estejam registrados e tenham memória suficiente". Definir spark.storage.memoryFraction como 0.1 também não pode resolver o problema. Talvez o problema esteja no meu código.Obrigado!
precisa saber é o seguinte

2
@samthebest Esta é uma resposta fantástica. Eu realmente aprecio a ajuda do registro para encontrar vazamentos de memória.
Myles Baker

1
Olá @samthebest, como você especificou 8000 partições? Como estou usando o Spark sql, só posso especificar partição usando spark.sql.shuffle.partitions, o valor padrão é 200, caso eu o configure mais, tentei configurá-lo para 1000, mas não ajudando a obter OOM, você sabe qual deve ser o ideal valor da partição Eu tenho dados distorcidos de 1 TB para processar e envolve agrupar por consultas de seção. Por favor, guie.
Umesh K

2
Olá @ user449355, você poderia fazer uma nova pergunta? Por medo de iniciar um longo tópico de comentários :) Se você está tendo problemas, provavelmente outras pessoas estão, e uma pergunta facilitaria a busca de todos.
precisa

1
Para o seu primeiro ponto, @samthebest, você não deve usar TODA a memória spark.executor.memoryporque definitivamente precisa de uma quantidade de memória para sobrecarga de E / S. Se você usar tudo isso, o programa ficará mais lento. A exceção a isso pode ser o Unix, nesse caso, você tem espaço de troca.
Hunle

58

Para adicionar um caso de uso a isso que geralmente não é discutido, apresentarei uma solução ao enviar um Sparkaplicativo via spark-submitno modo local .

De acordo com o gitbook Mastering Apache Spark de Jacek Laskowski :

Você pode executar o Spark no modo local. Nesse modo de implantação de JVM única não distribuída, o Spark gera todos os componentes de execução - driver, executor, back-end e mestre - na mesma JVM. Este é o único modo em que um driver é usado para execução.

Portanto, se você estiver enfrentando OOMerros com o heap, basta ajustar o driver-memorye não o executor-memory.

Aqui está um exemplo:

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 porcentagem devemos considerar para a memória do driver no modo autônomo.
Yashwanth Kambala 04/10/19

@Brian, No modo local, a memória do driver precisa ser maior que o tamanho dos dados de entrada? É possível especificar o número de partições para o conjunto de dados de entrada, para que o trabalho do Spark possa lidar com um conjunto de dados muito maior que a RAM disponível?
fuyi 23/06

19

Você deve definir as configurações de memória offHeap, conforme mostrado abaixo:

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

Forneça a memória do driver e do executor conforme a disponibilidade de RAM da sua máquina. Você pode aumentar o tamanho offHeap se ainda estiver enfrentando o problema OutofMemory .


Definição Adicionado offHeap ajudou
kennyut

2
configurar a memória do driver em seu código não funcionará, leia a documentação do spark para isso: as propriedades do Spark podem ser divididas principalmente em dois tipos: um está relacionado à implantação, como "spark.driver.memory", "spark.executor.instances", esse tipo de propriedades pode não ser afetado ao definir programaticamente através do SparkConf em tempo de execução, ou o comportamento depende de qual gerenciador de cluster e modo de implantação você escolhe, portanto, é recomendável definir através do arquivo de configuração ou das opções da linha de comando de envio de spark.
Abdulhafeth Sartawi 27/01/19

1
A MELHOR RESPOSTA! Meu problema era que o Spark não estava instalado no nó mestre, apenas usei o PySpark para conectar-se ao HDFS e obtive o mesmo erro. Usando configresolveu o problema.
Mikhail_Sam

Acabei de adicionar as configurações usando o comando spark-submit para corrigir o problema de tamanho da pilha. Obrigado.
Pritam Sadhukhan

16

Você deve aumentar a memória do driver. Na sua pasta $ SPARK_HOME / conf, você deve encontrar o arquivo spark-defaults.conf, editar e definir o que spark.driver.memory 4000mdepende da memória do seu mestre, eu acho. Foi isso que corrigiu o problema para mim e tudo corre sem problemas


Quanto percentagem de mem a ser atribuído, em autônomo
Yashwanth Kambala

14

Dê uma olhada nos scripts de inicialização em que um tamanho de heap Java está definido, parece que você não está definindo isso antes de executar o trabalhador do 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"

Você pode encontrar a documentação para implantar scripts aqui .


Obrigado ~ Vou tentar mais tarde. No spark ui, ele mostra que a memória de cada executor é 4096. Então a configuração foi ativada, certo?
precisa saber é o seguinte

Vi sua resposta enquanto estou enfrentando um problema semelhante ( stackoverflow.com/questions/34762432/… ). Olhar para o link que você forneceu parece definir Xms / Xmx não existe mais, você pode dizer por quê?
Seffy

start up scriptsInfelizmente, o conteúdo do script vinculado por foi alterado. Nenhuma opção existe até 19/12/2019
David Groomes

7

Sofri muito com esse problema, usamos alocação dinâmica de recursos e achei que ele utilizaria meus recursos de cluster para melhor se adequar ao aplicativo.

Mas a verdade é que a alocação dinâmica de recursos não define a memória do driver e mantém seu valor padrão que é 1g.

Eu o resolvi definindo spark.driver.memory para um número adequado à memória do meu driver (para 32 GB de RAM, eu o configurei para 18 GB)

você pode configurá-lo usando o comando spark submit da seguinte maneira:

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

Nota muito importante, essa propriedade não será levada em consideração se você a definir a partir do código, de acordo com a documentação do spark:

As propriedades do Spark podem ser divididas principalmente em dois tipos: um está relacionado à implantação, como "spark.driver.memory", "spark.executor.instances", esse tipo de propriedades pode não ser afetado ao definir programaticamente através do SparkConf em tempo de execução ou o comportamento depende de qual gerenciador de cluster e modo de implementação você escolher; portanto, é recomendável definir o arquivo de configuração ou as opções da linha de comando de envio por spark; outro está relacionado principalmente ao controle de tempo de execução do Spark, como "spark.task.maxFailures", esse tipo de propriedades pode ser definido de qualquer maneira.


2
Você deve usar --conf spark.driver.memory = 18g
Merenptah

5

Em termos gerais, a memória JVM do Spark Executor pode ser dividida em duas partes. Memória Spark e memória do usuário. Isso é controlado pela propriedade spark.memory.fraction- o valor está entre 0 e 1. Ao trabalhar com imagens ou ao realizar processamento intensivo de memória em aplicativos spark, considere diminuir o valor spark.memory.fraction. Isso disponibilizará mais memória para o seu aplicativo. O Spark pode derramar, por isso ainda funcionará com menos compartilhamento de memória.

A segunda parte do problema é a divisão do trabalho. Se possível, particione seus dados em pedaços menores. Dados menores possivelmente precisam de menos memória. Mas se isso não for possível, você estará sacrificando a computação pela memória. Normalmente, um único executor estará executando vários núcleos. A memória total dos executores deve ser suficiente para lidar com os requisitos de memória de todas as tarefas simultâneas. Se aumentar a memória do executor não for uma opção, você poderá diminuir os núcleos por executor para que cada tarefa obtenha mais memória para trabalhar. Teste com 1 executores principais que possuam a maior memória possível e continue aumentando os núcleos até encontrar a melhor contagem de núcleos.


5

Você despejou seu mestre gc log? Então, eu encontrei um problema semelhante e achei que SPARK_DRIVER_MEMORY definia apenas o heap Xmx. O tamanho inicial do heap permanece 1G e o tamanho do heap nunca aumenta para o heap Xmx.

Passar "--conf" spark.driver.extraJavaOptions = -Xms20g "resolve meu problema.

ps aux | grep java e você verá o seguinte log: =

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

O local para definir o tamanho do heap da memória (pelo menos no spark-1.0.0) é em conf / spark-env. As variáveis ​​relevantes são SPARK_EXECUTOR_MEMORY& SPARK_DRIVER_MEMORY. Mais documentos estão no guia de implantação

Além disso, não esqueça de copiar o arquivo de configuração para todos os nós escravos.


4
Como você sabe qual ajustar entre SPARK_EXECUTOR_MEMORY& SPARK_DRIVER_MEMORY?
Hunle

13
ou seja, que erro lhe diria para aumentar o SPARK_EXECUTOR_MEMORYe que erro lhe diria para aumentar SPARK_DRIVER_MEMORY?
Hunle

2

Tenho poucas sugestões para o erro mencionado acima.

● Verifique se a memória do executor atribuída como executor pode ter que lidar com partições que exigem mais memória do que a que está atribuída.

● Tente verificar se mais shuffles estão ativos, pois as operações são caras, pois envolvem E / S de disco, serialização de dados e E / S de rede

● Usar junções de transmissão

● Evite usar groupByKeys e tente substituir por ReduceByKey

● Evite usar grandes objetos Java onde quer que ocorra a reprodução aleatória


Desculpe invadir a consulta de outra pessoa, mas como usar o reduzirByKey em vez de groupBy?
Somil Aseeja 29/11/19

1

Pelo meu entendimento do código fornecido acima, ele carrega o arquivo, mapeia a operação e salva de volta. Não há operação que exija a reprodução aleatória. Além disso, não há operação que exija que os dados sejam trazidos para o driver, portanto, o ajuste de qualquer coisa relacionada ao shuffle ou driver pode não ter impacto. O driver tem problemas quando há muitas tarefas, mas isso foi apenas até a versão 2.0.2. Pode haver duas coisas que estão dando errado.

  • Existem apenas um ou alguns executores. Aumente o número de executores para que possam ser alocados a diferentes escravos. Se você estiver usando o yarn, precisará alterar a configuração de num-executors ou se estiver usando o spark standalone, precisará ajustar o número de núcleos por executor e o spark max core conf. Em um número independente de executores = máximo de núcleos / núcleos por executor.
  • O número de partições é muito pequeno ou talvez apenas um. Portanto, se isso for baixo, mesmo se tivermos vários núcleos, vários executores, não será de grande ajuda, pois a paralelização depende do número de partições. Portanto, aumente as partições executando imageBundleRDD.repartition (11)

0

Definir essas configurações exatas ajudou a resolver o problema.

spark-submit --conf spark.yarn.maxAppAttempts=2 --executor-memory 10g --num-executors 50 --driver-memory 12g
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.