web-dev-qa-db-fra.com

Le JVM peut-il récupérer d'un OutofMemoryError sans redémarrer

  1. Le JVM peut-il récupérer d'un OutofMemoryError sans redémarrer s'il a une chance d'exécuter la GC avant d'autres demandes d'allocation d'objet arrivent?

  2. Les différentes implémentations JVM diffèrent-elles dans cet aspect?

Ma question concerne la récupération JVM et non le programme utilisateur qui tente de récupérer en attrapant l'erreur. En d'autres termes, si une OOMe est lancée dans un serveur d'applications (JBoss/WebSphere/..) Dois-je avez pour le redémarrer? Ou puis-je le laisser courir si d'autres demandes semblent fonctionner sans problème.

45
sengs

Cela peut fonctionner, mais c'est généralement une mauvaise idée. Il n'y a aucune garantie que votre application sera réussir En récupérant, ou qu'elle saura si elle n'a pas réussi. Par exemple:

  • Il peut vraiment y avoir non assez de mémoire pour effectuer les tâches demandées, même après avoir pris des mesures de récupération, telles que libérer le bloc de mémoire réservée. Dans cette situation, votre application peut être bloquée dans une boucle où elle apparaît à plusieurs reprises de récupérer puis de nouveau à nouveau en mémoire.

  • L'Oome peut être jeté sur n'importe quel fil. Si un thread ou une bibliothèque d'application n'est pas conçu pour y faire face, cela pourrait laisser une structure de données de longue durée dans un état incomplet ou incohérent.

  • Si les threads meurent à la suite de l'Oome, l'application peut avoir besoin de les redémarrer dans le cadre de la récupération de l'Oome. À tout le moins, cela rend la demande plus compliquée.

  • Supposons qu'un fil se synchronise avec d'autres threads en utilisant notifier/attendre ou un mécanisme de niveau supérieur. Si ce fil meurt d'un Oome, d'autres threads peuvent être laissés à l'attente de notification (etc.) qui ne viennent jamais ... Par exemple. La conception pour cela pourrait rendre la demande de manière significative plus compliquée.

En résumé, la conception, la mise en œuvre et la mise en œuvre d'une application à la récupération de l'OOMES peut être difficile, surtout si l'application (ou le cadre dans lequel il fonctionne, ou l'une des bibliothèques utilisées) est multi-fileté. C'est une meilleure idée de traiter Oome comme une erreur fatale.

Voir aussi ma réponse à une question connexe:

[~ # ~] Edit [~ # ~] - En réponse à cette question suivante:

En d'autres termes, si un Oome est lancé dans un serveur d'applications (JBoss/WebSphere/..) Dois-je devez Redémarrez-le?

Non, vous ne devez pas avoir à redémarrer. Mais c'est probablement sage à, surtout si vous n'avez pas de manière bonne/automatisée de vérifier que le service fonctionne correctement.

Le JVM va récupérer juste bien. Mais le serveur d'applications et l'application elle-même peuvent ne pas récupérer, en fonction de la manière dont ils sont conçus pour faire face à cette situation. (Mon expérience est que certains serveurs d'applications sont non Conçu pour faire face à cela, et que la conception et la mise en œuvre d'une application compliquée à récupérer de l'oomes est difficile et la teste correctement est encore plus difficile.)

Edit 2

En réponse à ce commentaire:

"D'autres threads peuvent être laissés à attendre des notifications (etc.) qui ne viennent jamais" vraiment? Le fil tué ne voudrait-il pas déranger ses piles, libérer des ressources comme cela se passe, y compris les serrures?

Oui vraiment! Considère ceci:

Le fil n ° 1 fonctionne ceci:

    synchronized(lock) {
         while (!someCondition) {
             lock.wait();
         }
    }
    // ...

Le fil n ° 2 fonctionne ceci:

    synchronized(lock) {
         // do stuff
         lock.notify();
    }

Si le thread n ° 1 attend sur la notification et que le thread n ° 2 obtient un Oome dans la section // do something, puis le thread n ° 2 ne rendra pas la fonction notify() appel, et le thread n ° 1 peut rester coincé à jamais une notification qui ne se produira jamais. Bien sûr, le fil n ° 2 est garanti de libérer le mutex sur l'objet lock ... mais ce n'est pas suffisant!

Si ce n'est pas le code, le fil du fil n'est pas en sécurité, ce qui est un problème plus général.

"Safe exceptionnel" n'est pas un terme que j'ai entendu parler de (bien que je sache ce que vous voulez dire). Java Les programmes ne sont normalement pas conçus pour être résilients à des exceptions inattendues. En effet, dans un scénario comme ce qui précède, il est susceptible d'être quelque part entre dur et impossible de faire en sécurité l'exception de l'application.

Vous auriez besoin d'un mécanisme selon lequel la défaillance du fil n ° 1 (en raison de l'Oome) est transformée en une notification de panne de communication inter-threads à thread n ° 2. Erlang fait cela ... mais pas Java. La raison pour laquelle ils peuvent le faire dans Erlang sont que les processus Erlang communiquent à l'aide de primitives strictes de type CSP; I.E. Il n'y a pas de partage de structures de données!

(Notez que vous pourriez obtenir le problème ci-dessus pour à peu près tout inattendu Exception ... pas seulement Error exceptions. Il existe certains types de Java code où tenter de récupérer d'un inattendu Exception est susceptible de se terminer mal.)

45
Stephen C

Le jvm sera exécuter le GC lorsqu'il est au bord de la OutOfMemoryError. Si le GC n'avait pas aidé du tout, le JVM lancera Oome.

Vous peut Cependant, catch IT et si nécessaire, prenez un chemin alternatif. Toute allocations à l'intérieur du bloc try sera GC'ed.

Depuis que l'Oome est "juste" un Error que vous pourriez juste catch, je m'attendrais à ce que les différentes implémentations de JVM se comportent de la même manière. Je peux au moins confirmer de l'expérience que ce qui précède est vrai pour le Sun JVM.

Voir aussi:

3
BalusC

Je dirais que cela dépend en partie de ce qui a provoqué l'OutofMemoryError. Si la JVM fonctionne vraiment bas sur la mémoire, il serait peut-être une bonne idée de le redémarrer et avec plus de mémoire si possible (ou une application plus efficace). Cependant, j'ai vu une bonne quantité de composants causés par l'allocation de matrices de 2 Go et telle. Dans ce cas, si c'est quelque chose comme une application Web J2EE, les effets de l'erreur doivent être contraints à cette application particulière et un redémarrage à l'échelle de la JVM ne ferait aucun bien.

3
Adam Crume

peut il récupérer? Peut-être. Toute JVM bien écrit ne va pas lancer un Oome après avoir essayé tout ce qu'il est possible de récupérer suffisamment de mémoire pour faire ce que vous le dites. Il y a une très bonne chance que cela signifie que vous ne pouvez pas récupérer. Mais...

Cela dépend de beaucoup de choses. Par exemple, si le collecteur des ordures n'est pas un collecteur de copie, la condition "hors de la mémoire" peut en réalité être "Aucun morceau assez gros restant à allouer". L'acte même de dérouler la pile peut avoir des objets nettoyés dans un tour de GC ultérieur qui laissent des morceaux ouverts assez gros pour vos besoins. Dans cette situation, vous pourrez peut-être redémarrer. Cela vaut probablement la peine d'être au moins réessayant une fois en conséquence. Mais...

Vous ne voulez probablement pas compter sur cela. Si vous obtenez un Oome avec une régularité, vous feriez mieux de regarder votre serveur et de découvrir ce qui se passe et pourquoi. Peut-être que vous devez nettoyer votre code (vous pourriez faire fuir ou faire trop d'objets temporaires). Peut-être que vous devez élever votre plafond de mémoire lorsque vous invoquez la JVM. Traitez l'Oome, même si elle est recouvrable, comme un signe que quelque chose de mauvais a frappé le fan quelque part dans votre code et agissez en conséquence. Peut-être que votre serveur n'a pas à descendre Nownownownowownow, mais vous devrez réparer quelque chose avant de passer des ennuis profonds.

Vous pouvez augmenter vos chances de récupération de ce scénario bien que ce n'est pas recommandé d'essayer. Ce que vous faites est de pré-allouer une quantité fixe de mémoire sur le démarrage, qui dédiée à votre travail de récupération, et lorsque vous attrapez l'OOM, NULL out cette référence pré-allouée et que vous êtes plus susceptible de avoir une mémoire à utiliser dans votre séquence de récupération.

Je ne connais pas différentes implémentations JVM.

1
Amir Afghani

Vous ne pouvez pas compléter à une JVM qui avait OutofMemoryError. Au moins avec l'oracle JVM, vous pouvez ajouter -XX:OnOutOfMemoryError="cmd args;cmd args" et prendre des mesures de récupération, comme tuer la JVM ou envoyer l'événement quelque part.

Référence: https://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-40102.html

0
i000174