É sempre bom capturar StackOverflowError em Java?


27

Eu costumava pensar que não, mas ontem eu tive que fazer isso. É um aplicativo que usa Akka (uma implementação de sistema de ator para a JVM) para processar tarefas assíncronas. Um dos atores realiza alguma manipulação de PDF e, como a biblioteca é de buggy, ela morre com um de StackOverflowErrorvez em quando.

O segundo aspecto é que o Akka está configurado para encerrar todo o sistema de atores se algum erro fatal da JVM (por exemplo, StackOverflowError) for detectado.

O terceiro aspecto é que esse sistema ator é incorporado a um aplicativo da Web (por motivos legados, WTF); portanto, quando o sistema do ator é desligado, o aplicativo da Web não é. O efeito líquido é que, em StackOverflowErrornosso aplicativo de processamento de tarefas, torna-se apenas um aplicativo Web vazio.

Como uma solução rápida, eu tive que capturar o StackOverflowErrorser jogado, para que o pool de threads do sistema de atores não fosse destruído. Isso me levou a pensar que talvez às vezes seja bom capturar esses erros, especialmente em contextos como este? Quando há um pool de threads processando tarefas arbitrárias? Ao contrário de um OutOfMemoryError, não consigo imaginar como um StackOverflowErroraplicativo pode deixar um estado inconsistente. A pilha é limpa após esse erro, para que o cálculo possa continuar normalmente. Mas talvez esteja perdendo algo importante.

Além disso, note que sou a favor de corrigir o erro em primeiro lugar (na verdade, eu já corrigi um SOE nesse mesmo aplicativo há alguns dias), mas eu realmente não sei quando isso tipo de situação pode surgir.

Por que seria melhor reiniciar o processo da JVM em vez de capturar a StackOverflowErrormarca, marcar a tarefa como falhada e continuar com meus negócios?

Existe alguma razão convincente para nunca pegar empresas estatais? Exceto "melhores práticas", que é um termo vago que não me diz nada.


1
outra opção seria a de aumentar o espaço de pilha disponível no JVM
aberração catraca

3
@ratchetfreak: StackOverflowExceptions geralmente são devidos a uma cadeia não terminável de chamadas de método - aumentar o espaço da pilha aumentaria o custo da memória de um novo thread sem nenhum benefício.
Jhominal 22/08

1
Pelo menos um SOE era legítimo porque a entrada era muito grande. Infelizmente, lidar com isso com uma implementação recursiva (regex do Java impl.) Não era uma boa idéia. De qualquer forma, mesmo quando o cálculo é garantido para terminar, você não sabe se o novo tamanho da pilha é grande o suficiente para outros cálculos.
Ionuț G. Stan

2
Isso não deveria ser migrado para Sta ... Oh, espere ... não importa. :-)
Blrfl

Em relação à sua biblioteca de buggy. Você realmente deve migrar a função de manipulação do pdf para o seu próprio processo, para que você possa matá-lo.
Esben Skov Pedersen

Respostas:


44

Como regra geral, se nunca fosse absolutamente aceitável fazer alguma coisa, e houvesse acordo sobre isso, os implementadores de linguagem não teriam permitido. Quase não existem máximas tão unanimemente claras. (Felizmente, porque é isso que nos mantém programadores humanos em empregos!)

Parece que você encontrou uma situação em que a captura desse erro é a melhor opção para você: permite que seu aplicativo funcione, enquanto todas as outras alternativas não, e é isso que conta no final. Todas as "melhores práticas" são simplesmente resumos de longas experiências com muitos casos que geralmente podem ser usados ​​no lugar de uma análise detalhada de um caso específico para economizar tempo; no seu caso, você já fez a análise específica e obteve um resultado diferente. Parabéns, você é capaz de pensar de forma independente!

(Dito isso, certamente há situações em que um estouro de pilha pode deixar um aplicativo inconsistente como uma exaustão de memória. Imagine que algum objeto seja construído e inicializado com a ajuda de chamadas de método internas aninhadas - se um deles jogar, o objeto pode muito bem estar em um estado que não deveria ser possível, como se uma alocação tivesse falhado. Mas isso não significa que sua solução ainda não possa ser a melhor.


3
Obrigado. Minhas dúvidas foram um pouco reforçadas depois que descobri que o .NET fez StackOverflowExceptionuma exceção não capturável. Eu sei, é uma plataforma diferente, mas achei que eles poderiam ter um motivo. Além disso, seu argumento em relação à inicialização do objeto é direto. Isso me leva a pensar que eu deveria pegar esse SOE algumas camadas de abstração abaixo, para não pegar o SOE "errado".
Ionuț G. Stan

14
+1: as práticas recomendadas sempre devem vir com explicações sobre por que e em que contexto elas são "melhores", para que você possa julgar se elas se aplicam ao seu caso específico.
22613 Michael Borgwardt

situations heredeveria ser situations where.
Servy

2

Não sei se existem riscos específicos da JVM aqui, mas no geral parece bastante sensato.

Por exemplo, existem algoritmos recursivos, como quicksort ingênuo, que possuem log(n)profundidade de pilha em um caso típico, mas, na pior das hipóteses, eles se degradam em profundidade nque podem explodir a pilha.

O pior caso é raro e improvável que aconteça novamente se você reiniciar a classificação no conjunto parcialmente classificado, portanto, faz muito sentido capturar a exceção de estouro de pilha quando isso acontece e reiniciar o trabalho em vez de tentar impedir que erros ocorram ou matem aplicação inteira.

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.