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.