Attraper java.lang.OutOfMemoryError?

101

Documentation pour java.lang.Errordit:

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.Errorc'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, OutOfMemoryErrorsera jeté à nouveau.

Donc, ma question est:

  1. Y a-t-il des scénarios du monde réel où la capture java.lang.OutOfMemoryErrorpourrait être une bonne idée?
  2. 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)?
Denis Bazhenov
la source
Pour votre première question, j'ajouterai que j'attraperai l'OutOfMemoryError afin de (au moins essayer de) informer l'utilisateur du problème. Auparavant, l'erreur n'était pas interceptée par la clause catch (Exception e) et aucun retour n'était affiché à l'utilisateur.
Josep Rodríguez López
1
Il existe des cas spécifiques, par exemple, l'allocation d'un tableau gigantesque, où l'on peut attraper l'erreur MOO autour de cette opération et récupérer raisonnablement bien. Mais placer le try / catch autour d'une grosse goutte de code et tenter de récupérer proprement et de continuer est probablement une mauvaise idée.
Hot Licks

Réponses:

85

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 OutOfMemoryErroret d'après mon expérience (sur les JVM Windows et Solaris), ce n'est que très rarement OutOfMemoryErrorle glas d'une JVM.

Il n'y a qu'une seule bonne raison pour attraper un OutOfMemoryErroret 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 OutOfMemoryErrorse 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 Errorest 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 OutOfMemoryErrorne signifie pas que la JVM est à court de mémoire dans le bloc catch:

private static final int MEGABYTE = (1024*1024);
public static void runOutOfMemory() {
    MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    for (int i=1; i <= 100; i++) {
        try {
            byte[] bytes = new byte[MEGABYTE*500];
        } catch (Exception e) {
            e.printStackTrace();
        } catch (OutOfMemoryError e) {
            MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
            long maxMemory = heapUsage.getMax() / MEGABYTE;
            long usedMemory = heapUsage.getUsed() / MEGABYTE;
            System.out.println(i+ " : Memory Use :" + usedMemory + "M/" + maxMemory + "M");
        }
    }
}

Sortie de ce code:

1 : Memory Use :0M/247M
..
..
..
98 : Memory Use :0M/247M
99 : Memory Use :0M/247M
100 : Memory Use :0M/247M

Si j'exécute quelque chose de critique, Errorj'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, Erroril 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 ça

Chris
la source
D'accord, je publierai mon expérience sur une nouvelle réponse.
Mister Smith
4
"vous ne pouvez jamais être sûr à 100% que la JVM est dans un état réparable": parce que le OutOfMemoryErrorpeut 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/…
Raedwald
Dans OpenJdk1.7.0_40, je n'obtiens aucune erreur ou exception lorsque j'exécute ce code. Même j'ai changé le MEGABYTE en GIGABYTE (1024 * 1024 * 1024). Est-ce parce que l'optimiseur supprime la variable «byte [] bytes» car elle n'est pas utilisée dans le reste du code?
RoboAlex
"Il existe un certain nombre de scénarios où vous pouvez souhaiter attraper une OutOfMemoryError" par rapport à "Il n'y a qu'une seule bonne raison d'attraper un OutOfMemoryError" . Décide toi!!!
Stephen C
3
Scénario du monde réel où vous pouvez vouloir attraper l'erreur OutOfMemory: lorsqu'elle est causée par une tentative d'allocation d'un tableau avec plus de 2G éléments. Le nom de l'erreur est un peu inapproprié dans ce cas, mais il s'agit toujours d'un MOO.
Charles Roth
31

Vous pouvez en récupérer:

package com.stackoverflow.q2679330;

public class Test {

    public static void main(String... args) {
        int size = Integer.MAX_VALUE;
        int factor = 10;

        while (true) {
            try {
                System.out.println("Trying to allocate " + size + " bytes");
                byte[] bytes = new byte[size];
                System.out.println("Succeed!");
                break;
            } catch (OutOfMemoryError e) {
                System.out.println("OOME .. Trying again with 10x less");
                size /= factor;
            }
        }
    }

}

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:

1: y a-t-il des scénarios de mots réels lors de la capture de java.lang.OutOfMemoryError peut être une bonne idée?

Aucun ne vient à l'esprit.

2: si nous attrapons java.lang.OutOfMemoryError, comment pouvons-nous nous assurer que le gestionnaire de capture n'alloue pas de mémoire par lui-même (aucun outil ou meilleures pratiques)?

Dépend de ce qui a causé le OOME. S'il est déclaré en dehors du trybloc 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:

private static byte[] reserve = new byte[1024 * 1024]; // Reserves 1MB.

puis mettez-le à zéro pendant OOME:

} catch (OutOfMemoryException e) {
     reserve = new byte[0];
     // Ha! 1MB free!
}

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.

BalusC
la source
1
Même en réservant de l'espace, cela ne garantit pas une solution fonctionnelle. Cet espace pourrait aussi être pris par un autre fil;)
Wolph
@Wolph: Donnez alors plus de mémoire à la JVM! O_o Tout avec tout cela n'a en effet aucun sens;)
BalusC
2
Le premier extrait de code fonctionne car l'objet qui a déclenché l'erreur est un seul objet BIG (le tableau). Lorsque la clause catch est atteinte, elle a été collectée par la JVM gourmande en mémoire. Si vous utilisiez le même objet en dehors du bloc try, ou dans un autre thread, ou dans le même catch, JVM ne le collecte pas, ce qui rend impossible la création d'un nouvel objet unique de quelque type que ce soit. le deuxième extrait, par exemple, peut ne pas fonctionner.
Mister Smith le
3
pourquoi ne pas simplement le régler null?
Pacerier
1
@MisterSmith Votre commentaire n'a aucun sens. Le grand objet n'existe pas. Il n'a pas été alloué en premier lieu: il a déclenché le MOO, donc il n'a certainement pas besoin de GC.
Marquis de Lorne
16

En général, c'est une mauvaise idée d'essayer d'attraper et de récupérer à partir d'un MOO.

  1. 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.

  2. 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:

  • ce qui a spécifiquement causé l'OOME,
  • ce que faisait l'application à ce moment-là, et qu'il est acceptable de simplement supprimer ce calcul, et
  • qu'un OOME (à peu près) simultané ne peut pas s'être produit sur un autre thread.

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.)

Stephen C
la source
Oui je suis d'accord avec toi. En général, c'est une mauvaise idée. Mais pourquoi alors j'ai la possibilité de l'attraper? :)
Denis Bazhenov
@dotsid - 1) parce qu'il y a des cas où vous devriez l'attraper, et 2) parce que rendre impossible la capture du MOO aurait un impact négatif sur le langage et / ou d'autres parties du runtime Java.
Stephen C
1
Vous dites: "parce qu'il y a des cas où vous devriez l'attraper". Cela faisait donc partie de ma question initiale. Quels sont les cas où vous voulez attraper OOME?
Denis Bazhenov
1
@dotsid - voir ma réponse modifiée. Le seul cas auquel je peux penser pour attraper un MOO est lorsque vous devez le faire pour forcer une application multithread à se fermer en cas de MOO. Vous voudrez peut-être faire cela pour tous les sous-types de Error.
Stephen C
1
Il ne s'agit pas simplement d'attraper des OOME. Vous devez également vous en remettre. Comment un thread récupère-t -il s'il était censé (dire) notifier un autre thread ... mais il a un OOME? Bien sûr, la JVM ne mourra pas. Mais l'application est susceptible de cesser de fonctionner en raison des threads bloqués en attente de notifications des threads qui ont redémarré en raison de la capture d'un OOME.
Stephen C
14

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 OutOfMemoryErroret 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.)

Michael Kuhn
la source
Alors vous avez du code comme byte[] bytes = new byte[length]? Pourquoi ne pas simplement vérifier sizeplus tôt?
Raedwald
1
Parce qu'il en sizeira de même avec plus de mémoire. Je passe par l'exception car dans la plupart des cas, tout ira bien.
Michael Kuhn
10

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.

Yishai
la source
8

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:

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); 
bitmapOptions.inSampleSize = 1;
boolean imageSet = false;
while (!imageSet) {
  try {
    image = BitmapFactory.decodeFile(filePath, bitmapOptions);
    imageView.setImageBitmap(image); 
    imageSet = true;
  }
  catch (OutOfMemoryError e) {
    bitmapOptions.inSampleSize *= 2;
  }
}

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.

Maria
la source
1
Une autre option serait de calculer la taille des bitmaps que vous pouvez gérer au lieu d'essayer et d'échouer. "Des exceptions devraient être utilisées pour des cas exceptionnels" - je pense que quelqu'un a dit. Mais je dirai que votre solution semble être la solution la plus simple, peut-être pas la meilleure, mais probablement la plus simple.
jontejj
Cela dépend du codec. Imaginez qu'un bmp de 10 Mo n'entraîne probablement qu'un peu plus de 10 Mo de tas, alors qu'un JPEG de 10 Mo «explosera». Idem dans mon cas où je souhaite analyser un XML qui peut varier massivement en fonction de la complexité du contenu
Daniel Alder
5

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:

-XX:+PrintCommandLineFlags
-XX:+PrintConcurrentLocks
-XX:+PrintClassHistogram

Voir la liste complète ici

Rob Heiser
la source
C'est encore pire que vous ne le pensez. Puisqu'une OutOfMemeoryError peut être lancée à n'importe quel point de votre programme (pas seulement à partir d'une newinstruction), votre programme sera dans un état indéfini lorsque vous attrapez l'exction.
Raedwald
5

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:

booléen isOutOfMemory = false; // indicateur utilisé pour le reporting
essayez {
   SomeType largeVar;
   // Boucle principale qui alloue de plus en plus à largeVar
   // peut terminer OK, ou déclencher OutOfMemoryError
}
catch (OutOfMemoryError ex) {
   // largeVar est maintenant hors de portée, tout comme garbage
   System.gc (); // nettoyer les données LargeVar
   isOutOfMemory = true; // drapeau disponible pour utilisation
}
// programme teste l'indicateur pour signaler la récupération

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.

Tony Simons
la source
Dans une seule application threadée, si vous n'utilisez plus certains des nouveaux objets problématiques dont la création a généré l'erreur, ceux-ci peuvent être collectés dans la clause catch. Cependant, si la JVM détecte que l'objet peut être utilisé plus tard, il ne peut pas être collecté et l'application explose. Voir ma réponse dans ce fil.
Mister Smith
4

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:

import java.util.LinkedList;
import java.util.List;

public class OOMErrorTest {             
    public static void main(String[] args) {
        List<Long> ll = new LinkedList<Long>();

        try {
            long l = 0;
            while(true){
                ll.add(new Long(l++));
            }
        } catch(OutOfMemoryError oome){         
            System.out.println("Error catched!!");
        }
        System.out.println("Test finished");
    }  
}

Cependant, le simple fait d'ajouter une seule ligne sur la capture vous montrera de quoi je parle:

import java.util.LinkedList;
import java.util.List;

public class OOMErrorTest {             
    public static void main(String[] args) {
        List<Long> ll = new LinkedList<Long>();

        try {
            long l = 0;
            while(true){
                ll.add(new Long(l++));
            }
        } catch(OutOfMemoryError oome){         
            System.out.println("Error catched!!");
            System.out.println("size:" +ll.size());
        }
        System.out.println("Test finished");
    }
}

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 llest 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 la System.out,printlnligne), 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.

Monsieur Smith
la source
Et si je fais ll=nullà l'intérieur du bloc catch?
Naanavanalla
3

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é.

cHao
la source
3

Pour la question 2, je vois déjà la solution que je suggérerais, par BalusC.

  1. Existe-t-il des scénarios de mots réels lors de la capture de java.lang.OutOfMemoryError peut être une bonne idée?

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.

Jarekczek
la source
3

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.

Jörg Eisfeld
la source
0
  1. Cela dépend de la façon dont vous définissez «bien». Nous faisons cela dans notre application Web boguée et cela fonctionne la plupart du temps (heureusement, OutOfMemorycela 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.
  2. Pour autant que je sache, le déroulement de la pile lourde en cours de route invalidera tant de références et libèrera ainsi tellement de mémoire que vous ne devriez pas vous en soucier.

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 OutOfMemoryErroret 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.

double
la source
-1

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.

TofuBière
la source
7
Une idée fausse que votre message perpétue est que le MOO indique que la JVM est à court de mémoire. Au lieu de cela, cela indique en fait que la JVM n'a pas pu allouer toute la mémoire à laquelle elle a été chargée. C'est-à-dire que si la JVM a 10B d'espace et que vous «new up» un objet 100B, il échouera, mais vous pourriez faire demi-tour et «new up» un objet 5B et tout se passerait bien.
Tim Bender
1
Et si je n'avais besoin que de 5B, pourquoi est-ce que je demande 10B? Si vous faites une allocation basée sur des essais et des erreurs, vous le faites mal.
TofuBeer
2
Je suppose que Tim voulait dire que vous pouviez encore effectuer du travail même avec une situation OutOfMemory. Il peut y avoir suffisamment de mémoire pour ouvrir une boîte de dialogue, par exemple.
Stephen Eilert