Quand System.gc () fait-il quelque chose?

118

Je sais que le ramassage des ordures est automatisé en Java. Mais j'ai compris que si vous appelez System.gc()votre code, la JVM peut décider ou non d'effectuer un ramasse-miettes à ce stade. Comment cela fonctionne-t-il précisément? Sur quelle base / paramètres exactement la JVM décide-t-elle de faire (ou de ne pas faire) un GC lorsqu'elle voit System.gc()?

Y a-t-il des exemples auquel cas c'est une bonne idée de mettre cela dans votre code?

Michael
la source
4
Un concept trop complexe pour être détaillé ici, mais cet article devrait vous aider: chaoticjava.com/posts/how-does-garbage-collection-work
GEOCHET
Le comportement exact dépend de la JVM, c'est-à-dire dépend de l'implémentation.
Thorbjørn Ravn Andersen

Réponses:

59

En pratique, il décide généralement de faire un garbage collection. La réponse varie en fonction de nombreux facteurs, comme la JVM sur laquelle vous utilisez, le mode dans lequel elle se trouve et l'algorithme de récupération de place utilisé.

Je n'en dépendrais pas dans votre code. Si la JVM est sur le point de lancer une OutOfMemoryError, appeler System.gc () ne l'arrêtera pas, car le garbage collector tentera de libérer autant que possible avant d'aller à cet extrême. La seule fois où je l'ai vu utilisé dans la pratique, c'est dans les IDE où il est attaché à un bouton sur lequel un utilisateur peut cliquer, mais même là, ce n'est pas très utile.

Jodonnell
la source
3
vous pouvez forcer un garbage collection dans jconsole
Benedikt Waldvogel
25
@bene Pouvez-vous vraiment le "forcer"? Est-ce que jconsole appelle juste System.gc ()?
John Meagher le
4
J'utiliserais System.gc()dans un écran de chargement pour un jeu vidéo. À ce stade, je ne me soucierais pas vraiment si l'appel effaçait tout ce qu'il pouvait, ou ne faisait rien du tout. Je voudrais cependant, préfère que « l' effort de recyclage expend vers les objets inutilisés » pendant l'écran de chargement, plutôt que pendant le gameplay.
Kröw
30

Le seul exemple auquel je peux penser où il est logique d'appeler System.gc () est lors du profilage d'une application pour rechercher d'éventuelles fuites de mémoire. Je crois que les profileurs appellent cette méthode juste avant de prendre un instantané de la mémoire.

Guillermo Vasconcelos
la source
4
Oui, c'est ce que je fais aussi. Les valeurs retournées par Runtime.freeMemory () par exemple ne sont vraiment significatives qu'après un System.gc (), car normalement vous voulez savoir combien de mémoire est bloquée par des instances non récupérables. À n'utiliser que dans le débogage / profilage de la mémoire bien sûr, jamais en production.
sleske
Non, c'est bon pour de nombreux autres scénarios aussi. Par exemple, disons que vous avez un modèle unique qui tient compte du cycle de vie. Dans onStop () si vous êtes null (ing) l'instance, c'est une bonne idée d'appeler System.gc () pour l'aider.
portfoliobuilder
26

La spécification du langage Java ne garantit pas que la JVM démarrera un GC lorsque vous appelez System.gc(). C'est la raison de ceci "peut ou non décider de faire un GC à ce stade".

Maintenant, si vous regardez le code source d'OpenJDK , qui est l'épine dorsale d'Oracle JVM, vous verrez qu'un appel à System.gc()démarre un cycle GC. Si vous utilisez une autre JVM, telle que J9, vous devez consulter leur documentation pour trouver la réponse. Par exemple, la JVM d'Azul a un garbage collector qui s'exécute en continu, donc un appel à System.gc()ne fera rien

Une autre réponse mentionne le démarrage d'un GC dans JConsole ou VisualVM. Fondamentalement, ces outils font un appel à distance à System.gc().

Habituellement, vous ne voulez pas démarrer un cycle de garbage collection à partir de votre code, car cela perturbe la sémantique de votre application. Votre application fait des affaires, la JVM s'occupe de la gestion de la mémoire. Vous devez garder ces préoccupations séparées (ne laissez pas votre application faire de la gestion de la mémoire, concentrez-vous sur les affaires).

Cependant, il y a peu de cas où un appel à System.gc()pourrait être compréhensible. Prenons, par exemple, les microbenchmarks. Personne ne veut qu'un cycle GC se produise au milieu d'un microbenchmark. Vous pouvez donc déclencher un cycle GC entre chaque mesure pour vous assurer que chaque mesure commence avec un tas vide.

Pierre Laporte
la source
26

Vous n'avez aucun contrôle sur GC en java - c'est la VM qui décide. Je n'ai jamais rencontré de cas où System.gc()c'est nécessaire . Puisqu'un System.gc()appel SUGGERE simplement que la VM fasse un garbage collection et qu'il effectue également un garbage collection COMPLET (anciennes et nouvelles générations dans un tas multigénérationnel), alors il peut en fait consommer PLUS de cycles CPU que nécessaire.

Dans certains cas, il peut être judicieux de suggérer à la machine virtuelle de faire une collecte complète MAINTENANT, car vous savez peut-être que l'application restera inactive pendant les prochaines minutes avant que de gros travaux ne se produisent. Par exemple, juste après l'initialisation d'un grand nombre d'objets temporaires lors du démarrage de l'application (c'est-à-dire que je viens de mettre en cache une tonne d'informations, et je sais que je n'aurai pas beaucoup d'activité pendant une minute ou deux). Pensez à un IDE tel que le démarrage d'Eclipse - il fait beaucoup pour s'initialiser, donc peut-être qu'immédiatement après l'initialisation, il est logique de faire un gc complet à ce stade.

DustinB
la source
17

Vous devez être très prudent si vous appelez System.gc(). L'appeler peut ajouter des problèmes de performances inutiles à votre application et il n'est pas garanti d'exécuter réellement une collecte. Il est en fait possible de désactiver explicite System.gc()via l'argument java -XX:+DisableExplicitGC.

Je vous recommande vivement de lire les documents disponibles sur Java HotSpot Garbage Collection pour plus de détails sur le garbage collection.

David Schlosnagle
la source
Mon application Android me lance toujours une OutOfMemoryException. Je pensais donc utiliser System.gc () dans la fonction onDestroy de ma classe pour effacer toutes les références et objets indésirables. Est-ce une bonne approche
Sagar Devanga
2
@Sagar Devanga L'appel manuel du gc comme vous le décrivez est peu susceptible d'aider. Avant de lancer un MOO, la JVM aura déjà tenté de ramasser des ordures afin de libérer de la mémoire
Scrubbie
10

System.gc()est implémenté par la VM, et ce qu'il fait est spécifique à l'implémentation. L'implémenteur pourrait simplement revenir et ne rien faire, par exemple.

Quant au moment d'émettre une collecte manuelle, le seul moment où vous voudrez peut-être le faire est lorsque vous abandonnez une grande collection contenant des tonnes de collections plus petites - un Map<String,<LinkedList>> par exemple - et que vous voulez essayer de prendre le coup de perf alors et là, mais pour la plupart, vous ne devriez pas vous en soucier. Le GC sait mieux que vous - malheureusement - la plupart du temps.

Patrick
la source
8

Si vous utilisez des tampons de mémoire directe, la JVM n'exécute pas le GC à votre place même si vous manquez de mémoire directe.

Si vous appelez ByteBuffer.allocateDirect()et que vous obtenez une OutOfMemoryError, vous pouvez trouver que cet appel convient après avoir déclenché manuellement un GC.

Peter Lawrey
la source
Êtes-vous en train de dire que System.gc () collecte les allocations directes alors que la JVM ne le fait normalement pas (avant de lancer un MOO). Parce que je pense que c'est faux. La seule raison pour laquelle une allocation peut réussir après un GC explicite est le temps supplémentaire et un peu plus de vraisemblance d'un objet dans le tas avec un finaliseur libéré (et libérant un objet directement alloué).
eckes
Dans la dernière version de Java 6, le code de allocateBuffer exécutera toujours un System.gc () avant de lancer une OutOfMemoryError. Dans le code de finalisation, il a un raccourci pour Cleanerslequel la mémoire directe utilise. c'est-à-dire qu'il n'est pas libéré par le fil du finaliseur car cela peut être trop lent. Même ainsi, il est possible d'obtenir un OOME si vous créez des régions de mémoire directe particulièrement rapidement. La solution est de ne pas faire cela et de réutiliser vos régions de mémoire directe là où vous le pouvez.
Peter Lawrey
1
merci, cela ressemble plus à mon expérience. Je suppose donc que la réponse n'est correcte que pour les anciennes versions de Java.
eckes
5

La plupart des JVM lancent un GC (en fonction du commutateur -XX: DiableExplicitGC et -XX: + ExplicitGCInvokesConcurrent). Mais la spécification est simplement moins bien définie afin de permettre de meilleures implémentations plus tard.

La spécification doit être clarifiée: Bogue # 6668279: (spec) System.gc () devrait indiquer que nous ne recommandons pas l'utilisation et ne garantissons pas le comportement

En interne, la méthode gc est utilisée par RMI et NIO, et elles nécessitent une exécution synchrone, ce qui: est actuellement en discussion:

Bogue # 5025281: Autoriser System.gc () à déclencher des collections complètes simultanées (et non stop-the-world)

eckes
la source
3

Garbage Collectionest bon Java, si nous exécutons un logiciel codé en java dans Desktop / ordinateur portable / serveur. Vous pouvez appeler System.gc()ou Runtime.getRuntime().gc()entrer Java.

Notez simplement qu'aucun de ces appels n'est garanti pour faire quoi que ce soit. Ils ne sont qu'une suggestion pour le jvm d'exécuter le Garbage Collector. C'est à la JVM qu'elle exécute le GC ou non. Donc, réponse courte: nous ne savons pas quand cela fonctionne. Réponse plus longue: JVM exécuterait gc si elle en avait le temps.

Je pense que c'est la même chose pour Android. Cependant, cela peut ralentir votre système.

Naveen
la source
JVM exécuterait gc si elle avait le temps pour cela : pas toujours vrai, Jvm peut exécuter gc lorsque la mémoire Eden est pleine.
abdelmouheimen
2

Normalement, la machine virtuelle ferait un garbage collection automatiquement avant de lancer une OutOfMemoryException, donc l'ajout d'un appel explicite ne devrait pas aider sauf en ce qu'il déplace peut-être l'atteinte de performance à un moment antérieur.

Cependant, je pense avoir rencontré un cas où cela pourrait être pertinent. Je ne suis pas sûr cependant, car je n'ai pas encore testé si cela a un effet:

Lorsque vous mappez en mémoire un fichier, je pense que l'appel map () lève une exception IOException lorsqu'un bloc de mémoire suffisamment grand n'est pas disponible. Un garbage collection juste avant le fichier map () pourrait aider à éviter cela, je pense. Qu'est-ce que tu penses?


la source
2

nous ne pouvons jamais forcer le ramassage des ordures. System.gc ne suggère que vm pour le garbage collection, cependant, personne ne sait vraiment à quelle heure le mécanisme s'exécute, c'est comme indiqué par les spécifications JSR.

lwpro2
la source
2

Il y a BEAUCOUP à dire en prenant le temps de tester les différents paramètres de ramasse-miettes, mais comme cela a été mentionné ci-dessus, il n'est généralement pas utile de le faire.

Je travaille actuellement sur un projet impliquant un environnement limité en mémoire et une quantité relativement importante de données - il y a quelques gros morceaux de données qui poussent mon environnement à sa limite, et même si j'ai pu réduire l'utilisation de la mémoire alors qu'en théorie cela devrait fonctionner très bien, j'obtiendrais toujours des erreurs d'espace de tas --- les options GC détaillées m'ont montré qu'il essayait de ramasser des ordures, mais en vain. Dans le débogueur, je pourrais exécuter System.gc () et bien sûr il y aurait "beaucoup" de mémoire disponible ... pas beaucoup de plus, mais assez.

Par conséquent, la seule fois où mon application appelle System.gc (), c'est lorsqu'elle est sur le point d'entrer dans le segment de code où les grands tampons nécessaires au traitement des données seront alloués, et un test sur la mémoire disponible disponible indique que je ne suis pas garanti de l'avoir. En particulier, je regarde un environnement de 1 Go où au moins 300 Mo sont occupés par des données statiques, la majeure partie des données non statiques étant liées à l'exécution, sauf lorsque les données en cours de traitement sont d'au moins 100 à 200 Mo à la source. Tout cela fait partie d'un processus de conversion automatique des données, donc les données existent toutes pour des périodes de temps relativement courtes à long terme.

Malheureusement, alors que les informations sur les différentes options pour régler le ramasse-miettes sont disponibles, cela semble en grande partie un processus expérimental et les détails de niveau inférieur nécessaires pour comprendre comment gérer ces situations spécifiques ne sont pas faciles à obtenir.

Tout cela étant dit, même si j'utilise System.gc (), j'ai continué à régler en utilisant les paramètres de ligne de commande et j'ai réussi à améliorer le temps de traitement global de mon application de manière relativement significative, malgré mon incapacité à surmonter le pierre d'achoppement posée par le travail avec les plus gros blocs de données. Cela étant dit, System.gc () est un outil ... un outil très peu fiable, et si vous ne faites pas attention à la façon dont vous l'utilisez, vous souhaiterez qu'il ne fonctionne pas plus souvent qu'autrement.

Kyune
la source
2

Si vous voulez savoir si votre System.gc()est appelé, vous pouvez avec la nouvelle mise à jour Java 7 mise à jour 4 recevoir une notification lorsque la JVM effectue le nettoyage de la mémoire.

Je ne suis pas sûr à 100% que la classe GarbageCollectorMXBean a été introduite dans la mise à jour 4 de Java 7, car je ne l'ai pas trouvée dans les notes de publication, mais j'ai trouvé les informations sur le site javaperformancetuning .com

Shervin Asgari
la source
0

Je ne peux pas penser à un exemple spécifique où il est bon d'exécuter GC explicite.

En général, exécuter un GC explicite peut en fait causer plus de mal que de bien, car un gc explicite déclenchera une collection complète, ce qui prend beaucoup plus de temps car il parcourt chaque objet. Si ce gc explicite finit par être appelé à plusieurs reprises, cela pourrait facilement conduire à une application lente car beaucoup de temps est passé à exécuter des GC complets.

Alternativement, si vous parcourez le tas avec un analyseur de tas et que vous suspectez qu'un composant de bibliothèque appelle des GC explicites, vous pouvez le désactiver en ajoutant: gc = -XX: + DisableExplicitGC aux paramètres JVM.

zxcv
la source
2
Nous appelons System.gc () pour essayer de fermer nos objets MappedByteBuffer qui autrement ne sont pas fermés, et donc non disponibles pour d'autres processus ou pour la troncature de fichiers, même si nous appelons 'close ()' sur les objets. Malheureusement, cela ne les ferme toujours pas.
Tim Cooper
0

Selon Bruce Eckel, un cas d'utilisation de l'appel explicite à System.gc () est celui où vous voulez forcer la finalisation, c'est-à-dire l'appel à la méthode de finalisation .

Astérisque
la source
NB: il existe une méthode propre pour forcer la finalisation. (Le problème n'est en fait pas d'exécuter des finaliseurs mais de reconnaître qu'un objet peut être finalisé car il n'est pas référencé)
eckes
-1

tandis que system.gc fonctionne, il arrêtera le monde: toutes les réponses sont arrêtées afin que le garbage collector puisse analyser chaque objet pour vérifier s'il est nécessaire de le supprimer. si l'application est un projet Web, toutes les demandes sont arrêtées jusqu'à ce que gc se termine, ce qui entraînera que votre projet Web ne pourra pas fonctionner en un instant.

fleture
la source