Documentation pour java.lang.Error
dit:
Une erreur est une sous-classe de Throwable qui indique des problèmes graves qu'une application raisonnable ne devrait pas essayer d'attraper
Mais comme java.lang.Error
c'est une sous-classe de java.lang.Throwable
, je peux attraper ce type de Throwable.
Je comprends pourquoi ce n'est pas une bonne idée d'attraper ce genre d'exception. Autant que je sache, si nous décidons de l'attraper, le gestionnaire de capture ne devrait pas allouer de mémoire par lui-même. Sinon, OutOfMemoryError
sera jeté à nouveau.
Donc, ma question est:
- Y a-t-il des scénarios du monde réel où la capture
java.lang.OutOfMemoryError
pourrait être une bonne idée? - Si nous décidons d'attraper
java.lang.OutOfMemoryError
, comment pouvons-nous nous assurer que le gestionnaire de capture n'alloue aucune mémoire par lui-même (des outils ou des meilleures pratiques)?
java
try-catch
out-of-memory
Denis Bazhenov
la source
la source
Réponses:
Je suis d'accord et en désaccord avec la plupart des réponses ici.
Il existe un certain nombre de scénarios dans lesquels vous souhaiterez peut-être attraper un
OutOfMemoryError
et d'après mon expérience (sur les JVM Windows et Solaris), ce n'est que très rarementOutOfMemoryError
le glas d'une JVM.Il n'y a qu'une seule bonne raison pour attraper un
OutOfMemoryError
et c'est de fermer gracieusement, de libérer proprement les ressources et de consigner au mieux la raison de l'échec (s'il est encore possible de le faire).En général, cela
OutOfMemoryError
se produit en raison d'une allocation de mémoire de bloc qui ne peut pas être satisfaite avec les ressources restantes du tas.Lorsque le
Error
est levé, le tas contient la même quantité d'objets alloués qu'avant l'allocation infructueuse et il est maintenant temps de supprimer les références aux objets d'exécution pour libérer encore plus de mémoire qui peut être nécessaire pour le nettoyage. Dans ces cas, il peut même être possible de continuer, mais ce serait certainement une mauvaise idée car vous ne pouvez jamais être sûr à 100% que la JVM est dans un état réparable.Démonstration qui
OutOfMemoryError
ne signifie pas que la JVM est à court de mémoire dans le bloc catch:Sortie de ce code:
Si j'exécute quelque chose de critique,
Error
j'attrape généralement le, je le connecte à syserr, puis je le connecte en utilisant le cadre de journalisation de mon choix, puis je libère les ressources et je le ferme de manière propre. Quel est le pire qui puisse arriver? La JVM est en train de mourir (ou déjà morte) de toute façon et en attrapant le,Error
il y a au moins une chance de nettoyage.La mise en garde est que vous devez cibler la capture de ces types d'erreurs uniquement dans les endroits où le nettoyage est possible. Ne pas couvrir
catch(Throwable t) {}
partout ou des bêtises comme çala source
OutOfMemoryError
peut avoir été jeté à partir d'un point qui a placé votre programme dans un état incohérent, car il peut être lancé à tout moment. Voir stackoverflow.com/questions/8728866/…Vous pouvez en récupérer:
Mais cela a-t-il un sens? Que souhaitez-vous faire d'autre? Pourquoi alloueriez-vous autant de mémoire au départ? Est-ce que moins de mémoire est également acceptable? Pourquoi ne l'utilisez-vous pas déjà de toute façon? Ou si ce n'est pas possible, pourquoi ne pas simplement donner plus de mémoire à la JVM dès le début?
Retour à vos questions:
Aucun ne vient à l'esprit.
Dépend de ce qui a causé le OOME. S'il est déclaré en dehors du
try
bloc et que cela s'est produit étape par étape, vos chances sont minces. Vous souhaiterez peut -être réserver de l'espace mémoire au préalable:puis mettez-le à zéro pendant OOME:
Bien sûr, cela n'a aucun sens;) Donnez simplement à la JVM suffisamment de mémoire pour répondre aux besoins de votre application. Exécutez un profileur si nécessaire.
la source
null
?En général, c'est une mauvaise idée d'essayer d'attraper et de récupérer à partir d'un MOO.
Un OOME peut également avoir été lancé sur d'autres threads, y compris des threads que votre application ne connaît même pas. Tous ces threads seront désormais morts et tout ce qui attendait une notification pourrait être bloqué à jamais. En bref, votre application pourrait être en phase terminale.
Même si vous réussissez à récupérer, votre JVM peut encore souffrir de la famine du tas et votre application fonctionnera de manière épouvantable en conséquence.
La meilleure chose à faire avec un OOME est de laisser mourir la JVM.
(Cela suppose que la machine virtuelle Java fait mourir. Par exemple sur un fil MOO de Tomcat servlet ne tuez pas la machine virtuelle Java, et cela conduit à la Tomcat d' entrer dans un état catatonique où il ne répondra pas aux demandes ... ne demande même redémarrer.)
ÉDITER
Je ne dis pas que c'est une mauvaise idée d'attraper le MOO. Les problèmes surviennent lorsque vous tentez ensuite de récupérer du OOME, soit délibérément, soit par erreur. Chaque fois que vous interceptez un MOO (directement ou en tant que sous-type d'erreur ou pouvant être renvoyé), vous devez soit le renvoyer, soit faire en sorte que l'application / JVM se ferme.
A part: Cela suggère que pour une robustesse maximale face aux MOO, une application devrait utiliser Thread.setDefaultUncaughtExceptionHandler () pour définir un gestionnaire qui provoquera la fermeture de l'application en cas de OOME, quel que soit le thread sur lequel l'OOME est lancé. Je serais intéressé par les opinions à ce sujet ...
Le seul autre scénario est celui où vous savez avec certitude que le MOO n'a causé aucun dommage collatéral; c'est à dire que vous savez:
Il existe des applications où il est possible de connaître ces choses, mais pour la plupart des applications, vous ne pouvez pas savoir avec certitude que la poursuite après un OOME est sûre. Même si cela «fonctionne» empiriquement lorsque vous l'essayez.
(Le problème est qu'il faut une preuve formelle pour montrer que les conséquences des OOME "anticipés" sont sûres, et que les OOME "imprévus" ne peuvent pas se produire sous le contrôle d'un try / catch OOME.)
la source
Error
.Oui, il existe des scénarios du monde réel. Voici le mien: j'ai besoin de traiter des ensembles de données de très nombreux éléments sur un cluster avec une mémoire limitée par nœud. Une instance JVM donnée parcourt de nombreux éléments les uns après les autres, mais certains éléments sont trop gros pour être traités sur le cluster: je peux attraper le
OutOfMemoryError
et prendre note des éléments trop gros. Plus tard, je peux relancer uniquement les gros éléments sur un ordinateur avec plus de RAM.(Comme il s'agit d'une allocation unique de plusieurs gigaoctets d'un tableau qui échoue, la JVM fonctionne toujours après avoir détecté l'erreur et il y a suffisamment de mémoire pour traiter les autres éléments.)
la source
byte[] bytes = new byte[length]
? Pourquoi ne pas simplement vérifiersize
plus tôt?size
ira de même avec plus de mémoire. Je passe par l'exception car dans la plupart des cas, tout ira bien.Il existe certainement des scénarios où attraper un OOME a du sens. IDEA les attrape et ouvre une boîte de dialogue pour vous permettre de modifier les paramètres de la mémoire de démarrage (puis se ferme lorsque vous avez terminé). Un serveur d'applications peut les attraper et les signaler. La clé pour ce faire est de le faire à un niveau élevé lors de l'envoi afin que vous ayez une chance raisonnable d'avoir un tas de ressources libérées au point où vous attrapez l'exception.
Outre le scénario IDEA ci-dessus, en général, la capture devrait être de Throwable, pas seulement de MOO spécifiquement, et devrait être effectuée dans un contexte où au moins le thread se terminera sous peu.
Bien sûr, la plupart du temps, la mémoire est affamée et la situation n'est pas récupérable, mais il y a des façons dont cela a du sens.
la source
Je suis tombé sur cette question parce que je me demandais si c'était une bonne idée d'attraper OutOfMemoryError dans mon cas. Je réponds ici en partie pour montrer encore un autre exemple où attraper cette erreur peut avoir un sens pour quelqu'un (c'est-à-dire moi) et en partie pour savoir si c'est une bonne idée dans mon cas en effet (avec moi étant un développeur junior uber, je ne peux jamais être trop sûr de toute ligne de code que j'écris).
Quoi qu'il en soit, je travaille sur une application Android qui peut être exécutée sur différents appareils avec différentes tailles de mémoire. La partie dangereuse consiste à décoder une image bitmap à partir d'un fichier et à l'afficher dans une instance ImageView. Je ne veux pas limiter les appareils les plus puissants en termes de taille de bitmap décodée, ni être sûr que l'application ne sera pas exécutée sur un appareil ancien que je n'ai jamais rencontré avec une mémoire très faible. Par conséquent, je fais ceci:
De cette façon, j'arrive à fournir des appareils plus ou moins puissants en fonction de leurs, ou plutôt de leurs besoins et attentes de leurs utilisateurs.
la source
Oui, la vraie question est "qu'allez-vous faire dans le gestionnaire d'exceptions?" Pour presque tout ce qui est utile, vous allouerez plus de mémoire. Si vous souhaitez effectuer un travail de diagnostic lorsqu'une erreur OutOfMemoryError se produit, vous pouvez utiliser le
-XX:OnOutOfMemoryError=<cmd>
hook fourni par la machine virtuelle HotSpot. Il exécutera vos commandes lorsqu'une OutOfMemoryError se produit, et vous pouvez faire quelque chose d'utile en dehors du tas de Java. Vous voulez vraiment empêcher l'application de manquer de mémoire en premier lieu, donc comprendre pourquoi cela se produit est la première étape. Ensuite, vous pouvez augmenter la taille du tas de MaxPermSize comme il convient. Voici quelques autres hooks HotSpot utiles:Voir la liste complète ici
la source
OutOfMemeoryError
peut être lancée à n'importe quel point de votre programme (pas seulement à partir d'unenew
instruction), votre programme sera dans un état indéfini lorsque vous attrapez l'exction.J'ai une application qui doit récupérer des échecs d'OutOfMemoryError, et dans les programmes à un seul thread, cela fonctionne toujours, mais parfois pas dans les programmes à plusieurs threads. L'application est un outil de test Java automatisé qui exécute les séquences de test générées à la profondeur maximale possible sur les classes de test. Désormais, l'interface utilisateur doit être stable, mais le moteur de test peut manquer de mémoire tout en développant l'arborescence des cas de test. Je gère cela par le type d'idiome de code suivant dans le moteur de test:
Cela fonctionne à chaque fois dans les applications à un seul thread. Mais j'ai récemment mis mon moteur de test dans un thread de travail distinct de l'interface utilisateur. Maintenant, la mémoire insuffisante peut se produire arbitrairement dans l'un ou l'autre des threads, et je ne sais pas comment l'attraper.
Par exemple, j'ai eu l'OOME pendant que les images d'un GIF animé dans mon interface utilisateur étaient cyclées par un thread propriétaire qui est créé en coulisses par une classe Swing qui est hors de mon contrôle. J'avais pensé que j'avais alloué toutes les ressources nécessaires à l'avance, mais il est clair que l'animateur alloue de la mémoire à chaque fois qu'il récupère l'image suivante. Si quelqu'un a une idée sur la façon de gérer les OOME soulevés dans n'importe quel fil, j'aimerais l'entendre.
la source
OOME peut être attrapé mais sera généralement inutile, selon que la JVM est capable de ramasser certains objets lorsque la capture est atteinte, et combien de mémoire de tas reste à ce moment-là.
Exemple: dans ma JVM, ce programme s'exécute jusqu'à la fin:
Cependant, le simple fait d'ajouter une seule ligne sur la capture vous montrera de quoi je parle:
Le premier programme fonctionne correctement car lorsque la capture est atteinte, la JVM détecte que la liste ne sera plus utilisée (cette détection peut également être une optimisation effectuée au moment de la compilation). Ainsi, lorsque nous atteignons l'instruction d'impression, la mémoire du tas a été libérée presque entièrement, nous avons donc maintenant une large marge de manœuvre pour continuer. C'est le meilleur des cas.
Cependant, si le code est organisé comme la liste
ll
est utilisée après que l'OOME a été capturé, la JVM ne peut pas le collecter. Cela se produit dans le deuxième extrait. Le OOME, déclenché par une nouvelle création Long, est attrapé, mais bientôt nous créons un nouvel Object (une String dans laSystem.out,println
ligne), et le tas est presque plein, donc un nouveau OOME est lancé. C'est le pire des cas: nous avons essayé de créer un nouvel objet, nous avons échoué, nous avons attrapé le OOME, oui, mais maintenant la première instruction nécessitant une nouvelle mémoire de tas (par exemple: créer un nouvel objet) lancera un nouveau OOME. Pensez-y, que pouvons-nous faire d'autre à ce stade avec si peu de mémoire ?. Probablement juste en train de sortir. D'où l'inutile.Parmi les raisons pour lesquelles la JVM ne collecte pas de ressources, l'une est vraiment effrayante: une ressource partagée avec d'autres threads l'utilisant également. N'importe qui avec un cerveau peut voir à quel point attraper OOME peut être dangereux s'il est inséré sur une application non expérimentale de tout type.
J'utilise une JVM Windows x86 32bits (JRE6). La mémoire par défaut pour chaque application Java est de 64 Mo.
la source
ll=null
à l'intérieur du bloc catch?La seule raison pour laquelle je peux penser à la détection des erreurs MOO pourrait être que vous avez des structures de données massives que vous n'utilisez plus, et que vous pouvez définir sur null et libérer de la mémoire. Mais (1) cela signifie que vous gaspillez de la mémoire et que vous devriez corriger votre code plutôt que de simplement boiter après un OOME, et (2) même si vous l'attrapiez, que feriez-vous? Le MOO peut se produire à tout moment, laissant potentiellement tout à moitié terminé.
la source
Pour la question 2, je vois déjà la solution que je suggérerais, par BalusC.
Je pense que je viens de tomber sur un bon exemple. Lorsque l'application awt distribue des messages, l'erreur OutOfMemoryError non corrigée est affichée sur stderr et le traitement du message actuel est arrêté. Mais l'application continue de fonctionner! L'utilisateur peut toujours émettre d'autres commandes sans être conscient des graves problèmes qui se produisent derrière la scène. Surtout quand il ne peut pas ou n'observe pas l'erreur standard. Il est donc souhaitable d'attraper une exception oom et de fournir (ou du moins suggérer) le redémarrage de l'application.
la source
J'ai juste un scénario où attraper une OutOfMemoryError semble avoir du sens et semble fonctionner.
Scénario: dans une application Android, je souhaite afficher plusieurs bitmaps dans la résolution la plus élevée possible et je souhaite pouvoir les zoomer couramment.
En raison du zoom fluide, je veux avoir les bitmaps en mémoire. Cependant, Android a des limitations de mémoire qui dépendent de l'appareil et qui sont difficiles à contrôler.
Dans cette situation, il peut y avoir OutOfMemoryError lors de la lecture du bitmap. Ici, cela aide si je l'attrape et que je continue avec une résolution inférieure.
la source
OutOfMemory
cela ne se produit plus en raison d'un correctif non lié). Cependant, même si vous l'attrapez, il se peut qu'il ait tout de même cassé du code important: si vous avez plusieurs threads, l'allocation de mémoire peut échouer dans l'un d'entre eux. Ainsi, en fonction de votre application, il y a encore 10 à 90% de chances qu'elle soit irréversiblement cassée.EDIT: Je vous suggère de l'essayer. Disons, écrivez un programme qui appelle récursivement une fonction qui alloue progressivement plus de mémoire. Catch
OutOfMemoryError
et voir si vous pouvez continuer de façon significative à partir de ce moment - là. D'après mon expérience, vous pourrez le faire, bien que dans mon cas, cela se soit produit sous le serveur WebLogic, il pourrait donc y avoir eu de la magie noire.la source
Vous pouvez attraper n'importe quoi sous Throwable, en général, vous ne devriez attraper que les sous-classes d'Exception à l'exclusion de RuntimeException (bien qu'une grande partie des développeurs attrapent également RuntimeException ... mais ce n'était jamais l'intention des concepteurs de langage).
Si vous deviez attraper OutOfMemoryError, que feriez-vous? La machine virtuelle est à court de mémoire, tout ce que vous pouvez faire est de quitter. Vous ne pouvez probablement même pas ouvrir une boîte de dialogue pour leur dire que vous êtes à court de mémoire car cela prendrait de la mémoire :-)
La VM lance une OutOfMemoryError quand elle est vraiment à court de mémoire (en effet, toutes les erreurs doivent indiquer des situations irrécupérables) et vous ne devriez vraiment rien faire pour y faire face.
Les choses à faire sont de découvrir pourquoi vous manquez de mémoire (utilisez un profileur, comme celui de NetBeans) et assurez-vous de ne pas avoir de fuites de mémoire. Si vous n'avez pas de fuites de mémoire, augmentez la mémoire que vous allouez à la machine virtuelle.
la source