Est-il * toujours * correct d'attraper StackOverflowError en Java?

27

Je pensais que non, mais hier, je devais le faire. Il s'agit d'une application qui utilise Akka (une implémentation de système d'acteur pour la JVM) pour traiter les travaux asynchrones. L'un des acteurs effectue une manipulation PDF, et parce que la bibliothèque est boguée, elle meurt de StackOverflowErrortemps en temps.

Le deuxième aspect est qu'Akka est configuré pour arrêter tout son système d'acteurs si une erreur fatale JVM (par exemple StackOverflowError) est détectée.

Le troisième aspect est que ce système d'acteur est intégré dans une application Web (pour WTF-ish, héritage, raisons), donc lorsque le système d'acteur est arrêté, l'application Web ne l'est pas. L'effet net est que sur StackOverflowErrornotre application de traitement des travaux devient juste une application Web vide.

En guise de solution rapide, j'ai dû attraper le StackOverflowErrorlancer, afin que le pool de threads du système d'acteurs ne soit pas démoli. Cela m'amène à penser que c'est peut-être parfois correct de détecter de telles erreurs, en particulier dans des contextes comme celui-ci? Lorsqu'il existe un pool de threads traitant des tâches arbitraires? Contrairement à un, OutOfMemoryErrorje ne peux pas imaginer comment un StackOverflowErrorpeut laisser une application dans un état incohérent. La pile est effacée après une telle erreur, donc le calcul peut continuer normalement. Mais peut-être que je manque quelque chose d'important.

De plus, notons que je suis tout à fait d'accord pour corriger l'erreur en premier lieu (en fait, j'ai déjà corrigé un SOE dans cette même application il y a quelques jours), mais je ne sais vraiment pas quand cela genre de situation pourrait survenir.

Pourquoi serait-il préférable de redémarrer le processus JVM au lieu de le rattraper StackOverflowError, de marquer ce travail comme ayant échoué et de poursuivre mon entreprise?

Y a-t-il une raison impérieuse de ne jamais attraper les entreprises publiques? Sauf «meilleures pratiques», qui est un terme vague qui ne me dit rien.

Ionuț G. Stan
la source
1
une autre option serait d'augmenter l'espace de pile disponible sur la JVM
ratchet freak
3
@ratchetfreak: les StackOverflowExceptions sont généralement dus à une chaîne non terminale d'appels de méthode - augmenter l'espace de la pile augmenterait alors le coût de la mémoire d'un nouveau thread sans aucun avantage.
jhominal
1
Au moins une entreprise publique était légitime car l'entrée était très importante. Malheureusement, la gérer avec une implémentation récursive (implication regex de Java) n'était pas une très bonne idée. Quoi qu'il en soit, même lorsque le calcul est assuré de se terminer, vous ne savez pas si la nouvelle taille de pile est suffisamment grande pour d'autres calculs.
Ionuț G. Stan
2
Ne devrait-on pas migrer vers Sta ... Oh, attendez ... tant pis. :-)
Blrfl
Concernant votre bibliothèque de buggy. Vous devriez vraiment migrer cette fonction de manipulation de pdf dans son propre processus afin de laisser le système d'exploitation le tuer.
Esben Skov Pedersen

Réponses:

44

En règle générale, s'il n'était absolument jamais, jamais acceptable de faire quelque chose, et qu'il y avait un accord à ce sujet, les implémenteurs de langage ne l'auraient pas autorisé. Il n'y a presque pas de maximes aussi claires à l'unanimité. (Heureusement, parce que c'est ce qui nous maintient en tant que programmeurs humains dans les emplois!)

Il semble que vous ayez trouvé une situation où la capture de cette erreur est la meilleure option pour vous: cela permet à votre application de fonctionner, contrairement à toutes les autres alternatives, et c'est ce qui compte au final. Toutes les "meilleures pratiques" ne sont que des résumés de longues expériences avec de nombreux cas qui peuvent généralement être utilisés à la place d'une analyse détaillée d'un cas spécifique pour gagner du temps; dans votre cas, vous avez déjà effectué l'analyse spécifique et obtenu un résultat différent. Félicitations, vous êtes capable d'une pensée indépendante!

(Cela dit, il y a sûrement des situations où un débordement de pile peut laisser une application incohérente tout comme l'épuisement de la mémoire. Imaginez simplement qu'un objet est construit puis initialisé à l'aide d'appels de méthodes internes imbriqués - si l'un d'eux lance, l'objet peut très bien être dans un état qui n'est pas censé être possible, tout comme si une allocation avait échoué. Mais cela ne signifie pas que votre solution ne pourrait pas encore être la meilleure.)

Kilian Foth
la source
3
Merci. Mes doutes ont été quelque peu renforcés après avoir découvert que .NET faisait StackOverflowExceptionune exception non capturable. Je sais, c'est une plateforme différente, mais je pensais qu'ils avaient peut-être une raison. En outre, votre point concernant l'initialisation d'objet est parfait. Cela m'amène à penser que je devrais attraper ce SOE quelques couches d'abstraction ci-dessous, afin que je n'attrape pas le "mauvais" SOE.
Ionuț G. Stan
14
+1: les meilleures pratiques doivent toujours être accompagnées d'explications expliquant pourquoi et dans quel contexte elles sont "les meilleures", afin que vous puissiez juger si elles s'appliquent à votre cas spécifique.
Michael Borgwardt
situations heredevrait être situations where.
Servy
2

Je ne sais pas s'il y a des risques spécifiques à la JVM ici, mais dans l'ensemble, cela semble assez raisonnable.

Par exemple, il existe des algorithmes récursifs, comme le tri rapide naïf, qui ont log(n)une profondeur de pile dans un cas typique, mais dans le pire des cas, ils se dégradent à une profondeur nqui peut faire exploser la pile.

Le pire des cas est rare, et il est peu probable que cela se reproduise si vous redémarrez le tri sur l'ensemble partiellement trié, il est donc très logique d'attraper une exception de dépassement de pile quand cela se produit et de redémarrer le travail au lieu d'essayer d'empêcher qu'une erreur ne se produise ou ne tue demande complète.

Kornel
la source