Comment forcer la collecte des ordures en Java?

225

Est-il possible de forcer le ramasse-miettes en Java, même si c'est délicat à faire? Je connais System.gc();et Runtime.gc();mais ils suggèrent seulement de faire GC. Comment puis-je forcer le GC?

Robert Columbia
la source
30
Il serait peut-être utile de fournir des informations sur les raisons pour lesquelles vous devez forcer GC. En règle générale, dans une langue garbage collection, il est incorrect d'appeler explicitement le collecteur.
Justin Ethier
3
Une JVM donnée peut fournir plusieurs méthodes de récupération de place, chacune avec ses propres avantages et inconvénients, et souvent une situation donnée peut être évitée simplement en faisant allusion à la JVM au démarrage. Veuillez expliquer le scénario.
Thorbjørn Ravn Andersen
3
jmap -histo: live <pid> stackoverflow.com/questions/6418089/…
5
Voici un cas d'utilisation pour forcer la récupération de place: j'ai un serveur avec un tas de 30 Go, dont environ 12 Go sont généralement utilisés (~ 5 millions d'objets). Toutes les 5 minutes, le serveur passe environ une minute à effectuer une tâche complexe dans laquelle environ 35 millions d'objets supplémentaires sont utilisés. Un GC complet est déclenché quelques fois par heure, invariablement pendant la tâche complexe, et gèle la machine virtuelle pendant 10 à 15 secondes. J'aimerais forcer le GC complet à s'exécuter à un moment où la tâche complexe n'est pas en cours d'exécution; ce serait alors jongler avec 5M d'objets vivants au lieu de 40M.
Steve
3
@JustinEthier Il y a un cas assez évident où vous voudrez peut-être forcer le GC, qui teste à l'unité tout comportement impliquant la hiérarchie de type java.lang.ref.Reference.
Elias Vasylenko

Réponses:

168

Votre meilleure option est d'appeler System.gc()ce qui est simplement un indice pour le garbage collector que vous voulez qu'il fasse une collecte. Il n'y a aucun moyen de forcer et de collecter immédiatement, car le garbage collector n'est pas déterministe.

Andrew Hare
la source
28
Il devrait y avoir. non-deterministic == trouble
Pacerier
7
Un ramasse-miettes peut être non déterministe et toujours offrir un moyen de forcer une collecte immédiate. Par exemple, le collecteur .NET n'est généralement pas déterministe, mais un appel à GC.Collect () l'oblige à s'exécuter. C'est juste que Java choisit de ne pas exposer cette fonction.
Petr Hudeček
2
D'après mon expérience, cette méthode invoque toujours le garbage collector. Il le fait avec suffisamment de régularité pour que mes tracés d'utilisation de la mémoire par rapport au nombre d'objets déclarés soient toujours strictement linéaires (en tenant compte du remplissage, etc.).
Jim Pivarski
Je pensais qu'en allouant de nouveaux objets et en ne les référençant plus, le garbage collector s'exécuterait automatiquement
Bionix1441
@ PetrHudeček Dans les applications réelles, .NET GC.Collect()ne recueille pas. En Java gc()fait.
ajeh
53

La bibliothèque jlibs a une bonne classe utilitaire pour la récupération de place . Vous pouvez forcer la collecte des ordures à l'aide d'une astuce astucieuse avec des objets WeakReference .

RuntimeUtil.gc () des jlibs:

   /**
    * This method guarantees that garbage collection is
    * done unlike <code>{@link System#gc()}</code>
    */
   public static void gc() {
     Object obj = new Object();
     WeakReference ref = new WeakReference<Object>(obj);
     obj = null;
     while(ref.get() != null) {
       System.gc();
     }
   }
simulacre
la source
1
Ce code est cassé car une référence faible est effacée dès que son référent devient faiblement accessible, c'est-à-dire avant d'être effacé de la mémoire.
Marko Topolnik
1
Vous pouvez confondre la signification de "GC a couru" avec "la mémoire a été récupérée". L'objet survit et n'est même pas encore finalisé, mais vous ne pouvez plus y accéder via la référence faible. Une façon un peu meilleure serait d'utiliser un PhantomReferenceavec un ReferenceQueueet ensuite vous seriez averti après la finalisation, mais toujours avant le nettoyage. Enfin, même si vous avez détecté avec succès que la mémoire de cet objet a été récupérée, cela signifierait encore très peu dans un GC générationnel comme HotSpot. Habituellement, cela coïncidait avec le nettoyage de la jeune génération.
Marko Topolnik
20
OP a demandé, et vous avez prétendu fournir, une solution pour "forcer la collecte des ordures". L'exécution du sous-système GC est une chose, la collecte des ordures en est une autre. L'exemple de code que vous avez fourni a clairement l'intention de garantir que les déchets ont été collectés. Quoi qu'il en soit, cette question étant très ancienne, il ne s'agit évidemment pas des souhaits d'OP, mais de l'utilité pour le grand public. Personne n'est intéressé à «forcer le sous-système du GC à s'exécuter» seul, sans aucune collecte de déchets. En fait, les gens veulent généralement une garantie que toutes les ordures ont été collectées.
Marko Topolnik
4
Vous ne l'avez probablement pas comparé à l'efficacité de System.gc(); System.gc();, mais il serait certainement intéressant de savoir si cela a jamais fonctionné mieux que cela. En fait, il suffit d’imprimer le nombre de fois qu’il a appelé System.gc(). La chance d'atteindre jamais 2 est assez mince.
Marko Topolnik
3
@MarkoTopolnik: "Personne n'est intéressé à" forcer le sous-système GC à s'exécuter "de lui-même, sans que des ordures soient collectées" .... En fait, je n'étais intéressé que par ce comportement aujourd'hui. Je suis reconnaissant que cette réponse soit présente. Mon objectif était de vérifier le comportement de rotation de la gestion des journaux du GC et d'obtenir le format de la sortie du GC. Cette petite astuce m'a aidé à remplir rapidement les journaux du GC.
erik.weathers
49

La meilleure (sinon la seule) façon de forcer un GC serait d'écrire une JVM personnalisée. Je crois que les collecteurs de déchets sont enfichables, vous pouvez donc probablement choisir l'une des implémentations disponibles et la modifier.

Remarque: ce n'est PAS une réponse facile.

Mainguy
la source
40
+1 pour lulz. rien ne rend le débogage frustrant meilleur que quelqu'un avec un sens de l'humour. autre qu'une réponse réellement utilisable, c'est.
2012
25

OUI il est presque possible de forcer vous devez appeler des méthodes dans le même ordre et en même temps celles-ci sont:

System.gc ();
System.runFinalization ();

même s'il n'y a qu'un seul objet à nettoyer, l'utilisation de ces deux méthodes en même temps force le garbage collector à utiliser la finalise()méthode de l'objet inaccessible en libérant la mémoire affectée et en faisant ce que la finalize()méthode indique.

TOUTEFOIS, c'est une pratique terrible d'utiliser le garbage collector car son utilisation pourrait introduire une surcharge du logiciel qui peut être pire que celle de la mémoire, le garbage collector a son propre thread qui n'est pas possible de contrôler plus en fonction de l'algorithme utilisé par le gc pourrait prendre plus de temps et est considéré comme très inefficace, vous devriez vérifier votre logiciel s'il est pire avec l'aide du gc car il est définitivement cassé, une bonne solution ne doit pas dépendre du gc.

REMARQUE: juste pour garder à l'esprit que cela ne fonctionnera que si dans la méthode de finalisation n'est pas une réaffectation de l'objet, si cela se produit, l'objet restera en vie et il aura une résurrection qui est techniquement possible.

legramira
la source
10
NON , même ces deux commandes ne forceront PAS une récupération de place. Comme déjà mentionné par d'autres, gc()n'est qu'un indice pour exécuter un garbage collection. runFinalizers()exécute uniquement les finaliseurs sur les objets "dont on a constaté qu'ils étaient rejetés". Si le GC n'a pas réellement fonctionné, il n'y a peut-être pas de tels objets ...
Steffen Heil
en outre, System.runFinalization () n'est pas une garantie que tout fonctionnera; il est possible que rien ne se produise du tout. C'est une suggestion - de Javadoc: "L' appel de cette méthode suggère que la machine virtuelle Java déploie des efforts pour exécuter les méthodes de finalisation des objets qui se sont avérés être rejetés mais dont les méthodes de finalisation n'ont pas encore été exécutées "
kaan
21

Dans la documentation d' OutOfMemoryError, il déclare qu'il ne sera pas jeté à moins que la machine virtuelle n'ait pas réussi à récupérer de la mémoire après un garbage collection complet. Donc, si vous continuez d'allouer de la mémoire jusqu'à ce que vous obteniez l'erreur, vous aurez déjà forcé une récupération de place complète.

Vraisemblablement, la question que vous vouliez vraiment poser était "comment puis-je récupérer la mémoire que je pense que je devrais récupérer par la collecte des ordures?"

Pete Kirkham
la source
18

Pour demander manuellement GC (pas à partir de System.gc ()):

  1. Allez dans: dossier bin dans JDK, par exemple-C: \ Program Files \ Java \ jdk1.6.0_31 \ bin
  2. Ouvrez jconsole.exe
  3. Connectez-vous au processus local souhaité.
  4. Accédez à l'onglet mémoire et cliquez sur effectuer GC.
Pinkesh Sharma
la source
3
Oppsss trompeur. Veuillez passer la souris sur le bouton "Exécuter GC". Vous pouvez demander à la machine virtuelle Java d'effectuer le GC mais ne forcez jamais.
Kumaran
@PinkeshSharma, cela ne force pas . C'est une simple demande qui pourrait probablement être complètement ignorée.
Pacerier
@Pacerier Dans un monde idéal oui .. mais si vous faites cela, vous verrez qu'il y a une augmentation de la mémoire instantanément ...
Pinkesh Sharma
11

.gc est un candidat à l'élimination dans les versions futures - un ingénieur Sun a un jour déclaré que peut-être moins de vingt personnes dans le monde savent réellement comment utiliser .gc () - j'ai fait du travail la nuit dernière pendant quelques heures sur une centrale / critique structure de données utilisant des données générées par SecureRandom, quelque part juste après 40 000 objets, la VM ralentirait comme si elle était à court de pointeurs. De toute évidence, il étouffait les tables de pointeurs 16 bits et présentait un comportement classique de "machines défaillantes".

J'ai essayé -Xms et ainsi de suite, j'ai continué à tourner jusqu'à ce qu'il atteigne environ 57, xxx quelque chose. Ensuite, il exécuterait gc allant de disons 57.127 à 57.128 après un gc () - à peu près au rythme du code au camp Easy Money.

Votre conception nécessite une refonte fondamentale, probablement une approche de fenêtre coulissante.

Nicholas Jordan
la source
1
J'ai quelque chose comme ça, beaucoup d'objets en mémoire, je ne peux pas les désallouer. L'exception OutOfMemory est levée, je veux forcer le GC à tester s'il existe un processus de création d'objets infinis ou si ces objets sont ceux utilisés par mon système.
On dirait que vous travaillez sur le même problème que moi, expliquez: "Création d'objet infini" ... bon projet de recherche, peut-être pouvez-vous poster une question ou quelque chose dans une zone java ici (je suis nouveau ici et non connaître le "Finite Automa" du fonctionnement du site) J'ai essayé hier et j'ai fini par faire file.dat car le compilateur se plaignait de "trop ​​de code" sur 40000 base36 BigIntegers codés comme une chaîne finale statique [] je vais coller mon cou ici et spécule que l'ensemble de la JVM est limité sur des pointeurs 16 bits, je parie que ce que nous devons faire est agressivement nul et lu à partir du disque ...
Nicholas Jordan
Vraiment, je ne vous comprends pas. Mais pour être clair sur "Infinite Object Creation", je voulais dire qu'il y a un morceau de code sur mon grand système qui crée des objets qui se manipulent et qui sont vivants en mémoire, je n'ai pas pu obtenir ce morceau de code en fait, juste un geste !!
5
Absurdité! Il y a un cas évident où il devrait être utilisé: tester du code qui utilise des références faibles, afin que nous puissions nous assurer que le comportement est correct lorsque les références faibles sont effacées.
Elias Vasylenko
6

La spécification JVM ne dit rien de spécifique sur la récupération de place. Pour cette raison, les fournisseurs sont libres d'implémenter GC à leur manière.

Ce flou entraîne donc une incertitude dans le comportement de collecte des ordures. Vous devriez vérifier les détails de votre JVM pour connaître les approches / algorithmes de récupération de place. Il existe également des options pour personnaliser le comportement.

rai.skumar
la source
4

Si vous devez forcer la récupération de place, vous devriez peut-être réfléchir à la façon dont vous gérez les ressources. Créez-vous de gros objets qui persistent dans la mémoire? Créez-vous des objets volumineux (par exemple, des classes graphiques) qui ont une Disposableinterface et n'appelez-vous pas dispose()lorsque vous en avez terminé? Déclarez-vous quelque chose au niveau de la classe dont vous n'avez besoin que dans une seule méthode?

Bob Kaufman
la source
2

Il serait préférable de décrire la raison pour laquelle vous avez besoin d'un ramasse-miettes. Si vous utilisez SWT, vous pouvez disposer de ressources telles que Imageet Fontpour libérer de la mémoire. Par exemple:

Image img = new Image(Display.getDefault(), 16, 16);
img.dispose();

Il existe également des outils pour déterminer les ressources non utilisées.

Paul Lammertsma
la source
que faire s'il n'y a pas de méthode d'élimination?
ArifMustafa
1
Complètement sans rapport avec la question! Non, je n'utilise pas SWT. J'appelle une méthode JNI qui ouvre une fenêtre .NET via une couche native Delphi. J'ai également un noyau de calcul FORTRAN qui reçoit des données via une couche C ++ native. Qu'est-ce que cela a à voir avec quoi que ce soit? Puis-je forcer un GC ou non? Non? :-(
Mostafa Zeinali
0

Si vous manquez de mémoire et que OutOfMemoryExceptionvous en obtenez un, vous pouvez essayer d'augmenter la quantité d'espace disponible sur java en démarrant votre programme avec java -Xms128m -Xmx512mau lieu de simplement java. Cela vous donnera une taille de tas initiale de 128 Mo et un maximum de 512 Mo, ce qui est bien plus que le 32 Mo / 128 Mo standard.

Viktor Dahl
la source
Les paramètres de mémoire par défaut sontjava -Xms512M -Xmx1024M
ThePyroEagle
0

Une autre option consiste à ne pas créer de nouveaux objets.

Le regroupement d'objets est loin pour réduire le besoin de GC en Java.

Le regroupement d'objets ne va généralement pas être plus rapide que la création d'objets (en particulier pour les objets légers) mais il est plus rapide que Garbage Collection. Si vous avez créé 10 000 objets et que chaque objet faisait 16 octets. C'est 160 000 octets que GC doit récupérer. D'un autre côté, si vous n'avez pas besoin de tous les 10000 en même temps, vous pouvez créer un pool pour recycler / réutiliser les objets, ce qui élimine le besoin de construire de nouveaux objets et élimine le besoin d'anciens objets GC.

Quelque chose comme ça (non testé). Et si vous voulez qu'il soit sûr pour les threads, vous pouvez échanger la LinkedList contre une ConcurrentLinkedQueue.

public abstract class Pool<T> {
    private int mApproximateSize;
    private LinkedList<T> mPool = new LinkedList<>();

    public Pool(int approximateSize) {
        mApproximateSize = approximateSize;
    }

    public T attain() {
        T item = mPool.poll();
        if (item == null) {
            item = newInstance();
        }
        return item;
    }

    public void release(T item) {
        int approxSize = mPool.size(); // not guaranteed accurate
        if (approxSize < mApproximateSize) {
            recycle(item);
            mPool.add(item);
        } else if (approxSize > mApproximateSize) {
            decommission(mPool.poll());
        }
    }

    public abstract T newInstance();

    public abstract void recycle(T item);

    public void decommission(T item) { }

}
Aaron T Harris
la source
0

Sur OracleJDK 10 avec G1 GC, un seul appel à System.gc()provoquera le nettoyage de l'ancienne collection par GC. Je ne sais pas si GC fonctionne immédiatement. Cependant, GC ne nettoiera pas la Young Collection même si elle System.gc()est appelée plusieurs fois dans une boucle. Pour que GC nettoie la Young Collection, vous devez allouer en boucle (par exemple new byte[1024]) sans appeler System.gc(). L'appel System.gc()pour une raison quelconque empêche GC de nettoyer la collection Young.

Nathan
la source
0

Vraiment, je ne vous comprends pas. Mais pour être clair sur "Infinite Object Creation", je voulais dire qu'il y a un morceau de code sur mon grand système qui crée des objets qui se manipulent et qui vivent en mémoire, je n'ai pas pu obtenir ce morceau de code en fait, juste un geste !!

C'est correct, seulement un geste. Vous avez à peu près les réponses standard déjà données par plusieurs affiches. Prenons celui-ci un par un:

  1. Je n'ai pas pu obtenir ce code

Exact, il n'y a pas de jvm réel - ce n'est qu'une spécification, un tas d'informatique décrivant un comportement souhaité ... J'ai récemment creusé l'initialisation des objets Java à partir du code natif. Pour obtenir ce que vous voulez, la seule façon est de faire ce que l'on appelle une annulation agressive. Les erreurs si mal faites sont si mauvaises que nous devons nous limiter à la portée initiale de la question:

  1. un morceau de code sur mon grand système crée des objets

La plupart des affiches ici supposeront que vous dites que vous travaillez sur une interface, si tel est le cas, nous devrions voir si vous recevez l'objet entier ou un élément à la fois.

Si vous n'avez plus besoin d'un objet, vous pouvez attribuer null à l'objet mais si vous vous trompez, une exception de pointeur null est générée. Je parie que vous pouvez obtenir un meilleur travail si vous utilisez NIO

Chaque fois que vous ou moi ou quelqu'un d'autre obtient: " S'il vous plaît, j'en ai horriblement besoin. " C'est un précurseur presque universel de la destruction presque totale de ce sur quoi vous essayez de travailler .... écrivez-nous un petit exemple de code, aseptisant de tout code réel utilisé et montrez-nous votre question.

Ne soyez pas frustré. Souvent, cela résout le problème: votre dba utilise un package acheté quelque part et la conception d'origine n'est pas modifiée pour les structures de données massives.

C'est très courant.

Nicholas Jordan
la source
-1

FYI

L'appel de méthode System.runFinalizersOnExit (true) garantit que les méthodes du finaliseur sont appelées avant l'arrêt de Java. Cependant, cette méthode est intrinsèquement dangereuse et a été déconseillée. Une alternative consiste à ajouter des «hooks d'arrêt» avec la méthode Runtime.addShutdownHook.

Masarrat Siddiqui

Masarrat Siddiqui
la source
Le problème avec les crochets d'arrêt est qu'ils fonctionnent rarement. La terminaison forcée ne fonctionne pas, le code de sortie non nul ne fonctionne pas, et parfois la JVM (officielle) ne les exécute tout simplement pas tant que vous en avez besoin.
ThePyroEagle
-1

Il existe un moyen indirect de forcer le ramasse-miettes. Il vous suffit de remplir le tas d'objets temporaires jusqu'au moment où le garbage collector s'exécutera. J'ai fait une classe qui force le garbage collector de cette façon:

class GarbageCollectorManager {

    private static boolean collectionWasForced;
    private static int refCounter = 0;

    public GarbageCollectorManager() {
        refCounter++;
    }

    @Override
    protected void finalize() {
        try {
            collectionWasForced = true;
            refCounter--;
            super.finalize();   
        } catch (Throwable ex) {
            Logger.getLogger(GarbageCollectorManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public int forceGarbageCollection() {
        final int TEMPORARY_ARRAY_SIZE_FOR_GC = 200_000;
        int iterationsUntilCollected = 0;
        collectionWasForced = false;

        if (refCounter < 2) 
            new GarbageCollectorManager();

        while (!collectionWasForced) {
            iterationsUntilCollected++;
            int[] arr = new int[TEMPORARY_ARRAY_SIZE_FOR_GC];
            arr = null;
        }

        return iterationsUntilCollected;
    }

}

Usage:

GarbageCollectorManager manager = new GarbageCollectorManager();
int iterationsUntilGcExecuted = manager.forceGarbageCollection();

Je ne sais pas à quel point cette méthode est utile, car elle remplit constamment le tas, mais si vous avez une application critique qui DOIT forcer GC - quand cela peut être la manière portable Java de forcer GC.

Agnius Vasiliauskas
la source
Qu'est-ce que "final int TEMPORARY_ARRAY_SIZE_FOR_GC = 200_000;"?
Koray Tugay
Taille du tableau - combien d'objets temporaires (int) seront générés pour que GC commence à fonctionner.
Agnius Vasiliauskas
Est-ce valide: "_" dans un entier?
Koray Tugay
1
Oui, les traits de soulignement dans les littéraux numériques sont valides à partir de Java SE 7. Ceci est utile par exemple comme séparateur de milliers en entier comme dans ce cas.
Agnius Vasiliauskas
3
Vous ne devez jamais exécuter un tel code dans un système de production. Bien que ce code s'exécute dans un thread, tout autre thread peut également obtenir une OutOfMemoryException, réversion complète de l'intention d'appeler cela en premier lieu ....
Steffen Heil
-1

Je voudrais ajouter quelque chose ici. Veuillez noter que Java ne fonctionne pas sur une machine virtuelle et non sur une machine réelle. La machine virtuelle a son propre moyen de communication avec la machine. Il peut varier d'un système à l'autre. Maintenant, lorsque nous appelons le GC, nous demandons à la machine virtuelle de Java d'appeler le garbage collector.

Étant donné que le garbage collector est avec une machine virtuelle, nous ne pouvons pas le forcer à effectuer un nettoyage sur place. Plutôt que nous mettons en file d'attente notre demande avec le garbage collector. Cela dépend de la machine virtuelle, après un temps particulier (cela peut changer d'un système à l'autre, généralement lorsque la mémoire de seuil allouée à la JVM est pleine), la machine réelle libérera de l'espace. :RÉ

Saubhagya Ranjan Das
la source
La première phrase du deuxième paragraphe est non séquentielle .
Marquis de Lorne
-1

Le code suivant est extrait de la méthode assertGC (...). Il essaie de forcer le ramasse-miettes non déterministe à collecter.

   List<byte[]> alloc = new ArrayList<byte[]>();
   int size = 100000;
   for (int i = 0; i < 50; i++) {
        if (ref.get() == null) {
             // Test succeeded! Week referenced object has been cleared by gc.
             return;
        }
        try {
             System.gc();
        } catch (OutOfMemoryError error) {
             // OK
        }
        try {
             System.runFinalization();
        } catch (OutOfMemoryError error) {
             // OK
        }

        // Approach the jvm maximal allocatable memory threshold
        try {
             // Allocates memory.
             alloc.add(new byte[size]);

             // The amount of allocated memory is increased for the next iteration.
             size = (int)(((double)size) * 1.3);
        } catch (OutOfMemoryError error) {
             // The amount of allocated memory is decreased for the next iteration.
             size = size / 2;
        }

        try {
             if (i % 3 == 0) Thread.sleep(321);
        } catch (InterruptedException t) {
             // ignore
        }
   }

   // Test failed! 

   // Free resources required for testing
   alloc = null;

   // Try to find out who holds the reference.
   String str = null;
   try {
        str = findRefsFromRoot(ref.get(), rootsHint);
   } catch (Exception e) {
        throw new AssertionFailedErrorException(e);
   } catch (OutOfMemoryError err) {
        // OK
   }
   fail(text + ":\n" + str);

Source (j'ai ajouté quelques commentaires pour plus de clarté): Exemple NbTestCase

H. Hess
la source
-1

Vous pouvez essayer d'utiliser Runtime.getRuntime().gc()ou d'utiliser une méthode utilitaire System.gc()Remarque: Ces méthodes ne garantissent pas le GC. Et leur portée doit être limitée à la JVM plutôt que de la gérer par programme dans votre application.

nabster
la source
2
Comme expliqué dans les autres réponses, ces méthodes ne forcent pas une exécution (complète) de la récupération de place.
Flow
-2

Si vous utilisez JUnit et Spring, essayez d'ajouter ceci dans chaque classe de test:

@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
Aliuk
la source