119 votes

Capture de java.lang.OutOfMemoryError?

Documentation pour l' 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 de l'attraper

Mais comme java.lang.Error est une sous-classe de java.lang.Throwable, je peux prendre ce type de Throwable.

Je comprends pourquoi ce n'est pas une bonne idée d'attraper ce type d'exception. Comme je le comprends, si on décide de l'attraper, le gestionnaire catch ne doit pas allouer de la mémoire par lui-même. Sinon, OutOfMemoryError sera relancée.

Donc, ma question est:

  1. Est-il vrai mot scénarios lors de la capture java.lang.OutOfMemoryError peut être une bonne idée?
  2. Si nous décidons de catch java.lang.OutOfMemoryError, comment pouvons-nous nous assurer que gestionnaire catch ne pas allouer de la mémoire en lui-même (les outils ou les meilleures pratiques)?

97voto

Chris Points 1616

Je suis d'accord et en désaccord avec la plupart des réponses ici.

Il y a un certain nombre de scénarios où vous pouvez attraper un OutOfMemoryError et dans mon expérience (sur Windows et Solaris Jvm), il est très rare que OutOfMemoryError est le glas pour une JVM.

Il y a une seule bonne raison pour attraper un OutOfMemoryError et c'est de fermer correctement, proprement, en libérant les ressources et la journalisation de la raison de l'échec du mieux que vous pouvez (si il est encore possible de le faire).

En général, la OutOfMemoryError se produit en raison d'un bloc d'allocation de mémoire qui ne peut être satisfaite avec le reste des ressources du tas.

Lorsque l'Erreur est levée le tas contient la même quantité d'objets alloués comme avant l'échec de l'allocation et c'est maintenant le temps de laisser tomber les références à l'exécution des objets pour libérer davantage 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 vraiment une mauvaise idée que vous ne pouvez jamais être certain à 100% que la JVM est dans une réparable état.

Démonstration que OutOfMemoryError ne signifie pas que la JVM est 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");
        }
    }
}

La 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 l'exécution de quelque chose de critique, j'ai l'habitude de capture de l'Erreur, connectez-vous à syserr, puis connectez-vous à l'aide de mon journalisation de son choix, puis procéder à la libération des ressources et de fermer proprement. Quelle est la pire chose qui puisse arriver? La JVM est en train de mourir (ou déjà morts) de toute façon et par la capture de l'Erreur il y a au moins une chance de nettoyage.

Le problème, c'est que vous avez pour objectif la capture de ces types d'erreurs que dans des endroits où le nettoyage est possible. Ne pas couverture de catch(Throwable t) {} partout ou le non-sens comme ça.

31voto

BalusC Points 498232

Vous pouvez la 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 fait-il sens? Quoi d'autre voulez-vous faire? Pourquoi voudriez-vous d'abord allouer beaucoup de mémoire? Est moins de mémoire aussi OK? Pourquoi n'avez-vous pas déjà de toute façon? Ou si ce n'est pas possible, pourquoi ne pas simplement donner de la JVM plus de mémoire depuis le début?

De retour à vos questions:

1: est-il vrai mot scénarios lors de la capture de java.lang.OutOfMemoryError peut être une bonne idée?

Aucun ne me vient à l'esprit.

2: si nous attraper java.lang.OutOfMemoryError comment pouvons-nous nous assurer que gestionnaire catch ne pas allouer de la mémoire en lui-même (les outils ou les meilleures pratiques)?

Dépend de ce qui a causé la OOME. Si elle est déclarée à l'extérieur de l' try bloc et il est arrivé à l'étape-par-étape, alors vos chances sont à peu. Vous mai souhaitez réserver de la mémoire de l'espace à l'avance:

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

et puis le mettre à zéro pendant OOME:

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

Bien sûr, cela rend le tout avec tous les pas de sens ;) Juste donner suffisamment de mémoire de la JVM comme votre applictation besoin. Exécuter un profiler, si nécessaire.

18voto

Stephen C Points 255558

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

  1. Un OOME pourrait aussi avoir été jeté sur d'autres threads, y compris les threads que votre application ne prend pas même le savoir. Ces fils seront désormais mort et tout ce qui était en attente sur un notify pourrait être bloqué pour toujours. En bref, votre application peut être en phase terminale cassé.

  2. Même si vous ne vous récupérer avec succès, votre JVM peut encore souffrir de segment de mémoire de la famine et de votre application fonctionnera lamentablement comme un résultat.

La meilleure chose à faire avec un OOME est de laisser la JVM mourir.

(Ce qui suppose que la JVM ne meurent. Par exemple, OOMs sur une servlet Tomcat thread ne pas tuer la JVM, ce qui conduit à la Tomcat entrer dans un état catatonique où il ne sera pas répondre à toutes les demandes ... même pas les demandes de redémarrer.)

MODIFIER

Je ne dis pas que c'est une mauvaise idée d'attraper OOM. Les problèmes surviennent lorsque vous tentez ensuite de récupérer à partir de la OOME, que ce soit délibérément ou par inadvertance. Chaque fois que vous attrapez un OOM (directement, ou comme un sous-type de l'Erreur ou de Throwable) vous devez renvoyer ou faire en sorte que l'application / JVM sorties.

De côté: Cela suggère que pour un maximum de robustesse face à des OOMs une application doit utiliser Thread.setDefaultUncaughtExceptionHandler() pour définir un gestionnaire qui sera la cause de la demande de sortie dans le cas d'une OOME, n'importe quel fil de la OOME est levée. Je serais intéressé par des avis sur cette ...

La seule autre scénario, c'est quand vous savez pour sûr que le OOM n'a pas entraîné de dommages collatéraux; c'est à dire, vous savez:

  • qu'est-ce qui a causé la OOME,
  • ce que l'application était en train de faire à l'époque, et que c'est OK pour jetez simplement que le calcul, et
  • qu'un (gros) simultanée OOME ne peut pas avoir eu lieu sur un autre thread.

Il y a des applications où il est possible de connaître ces choses, mais pour la plupart des applications, vous ne pouvez pas savoir à coup sûr.

16voto

Michael Kuhn Points 1449

Oui, il y a des scénarios du monde réel. Voici la mienne: j'ai besoin de traiter des ensembles de données de très nombreux éléments sur un cluster avec peu de mémoire par nœud. Une JVM instances passe par de nombreux éléments l'un après l'autre, mais certains de ces articles sont trop gros à gérer sur le cluster: je peux saisir l' OutOfMemoryError et de prendre note des éléments qui sont trop gros. Plus tard, je peux ré-exécuter seulement les articles de grande taille sur un ordinateur avec plus de RAM.

(Parce que c'est un seul de plusieurs giga-octets d'allocation d'un tableau qui échoue, la JVM est encore bien après la capture de l'erreur et il y a assez de mémoire pour traiter les autres éléments.)

10voto

Yishai Points 42417

Il y a certainement des scénarios où la capture d'un OOME de sens. IDÉE captures et affiche une boîte de dialogue pour vous permettre de modifier le démarrage de l'paramètres de la mémoire (et puis quitte lorsque vous avez terminé). Un serveur d'application peut attraper et de les signaler. La clé pour cela est de le faire à un niveau élevé sur l'expédition de sorte que vous avez une chance raisonnable d'avoir un tas de ressources libérées au point où vous en êtes la capture de l'exception.

D'ailleurs l'IDÉE du scénario ci-dessus, en général, la capture devrait être de Throwable, pas seulement OOM en particulier, et doit être fait dans un contexte où, au moins, le thread sera terminé sous peu.

Bien sûr, la plupart du temps de la mémoire est insuffisante et la situation n'est pas récupérable, mais il ya des façons que cela a du sens.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X