Après avoir répondu à une question sur la façon de forcer la libération d' objets en Java (le gars effaçait un HashMap de 1,5 Go) avec System.gc()
, on m'a dit que c'était une mauvaise pratique d'appeler System.gc()
manuellement, mais les commentaires n'étaient pas entièrement convaincants. De plus, personne ne semblait oser voter positivement, ni diminuer ma réponse.
On m'a dit que c'était une mauvaise pratique, mais ensuite on m'a également dit que les courses de ramasse-miettes n'arrêtaient plus systématiquement le monde, et que cela pouvait aussi être utilisé par la JVM comme un indice, donc je suis un peu à perte.
Je comprends que la JVM sait généralement mieux que vous quand elle a besoin de récupérer de la mémoire. Je comprends également que s'inquiéter de quelques kilo-octets de données est idiot. Je comprends également que même les mégaoctets de données ne sont pas ce qu'ils étaient il y a quelques années. Mais quand même, 1,5 gigaoctet? Et vous savez qu'il y a environ 1,5 Go de données en mémoire; ce n'est pas comme si c'était un coup dans le noir. Est-ce System.gc()
systématiquement mauvais ou y a-t-il un moment où ça va bien?
La question est donc en fait double:
- Pourquoi est-ce ou non une mauvaise pratique d'appeler
System.gc()
? S'agit-il vraiment d'un simple indice pour la machine virtuelle Java dans certaines implémentations, ou s'agit-il toujours d'un cycle de collecte complet? Existe-t-il vraiment des implémentations de garbage collector qui peuvent faire leur travail sans arrêter le monde? Veuillez faire la lumière sur les diverses affirmations que les gens ont faites dans les commentaires de ma réponse . - Où est le seuil? N'est-ce jamais une bonne idée d'appeler
System.gc()
ou y a-t-il des moments où c'est acceptable? Si oui, quels sont ces moments?
la source
Réponses:
La raison pour laquelle tout le monde dit toujours d'éviter
System.gc()
est que c'est un assez bon indicateur de code fondamentalement cassé . Tout code qui en dépend pour l'exactitude est certainement cassé; ceux qui en dépendent pour les performances sont très probablement cassés.Vous ne savez pas quel type de ramasse-miettes vous utilisez. Il y a certainement certains qui "n'arrêtent pas le monde" comme vous l'affirmez, mais certaines JVM ne sont pas si intelligentes ou pour diverses raisons (peut-être qu'elles sont sur un téléphone?) Ne le font pas. Tu ne sais pas ce que ça va faire.
De plus, il n'est pas garanti de faire quoi que ce soit. La machine virtuelle Java peut simplement ignorer entièrement votre demande.
La combinaison de «vous ne savez pas ce que cela fera», «vous ne savez pas si cela vous aidera,» et «vous ne devriez pas avoir besoin de l'appeler de toute façon» est la raison pour laquelle les gens sont si énergiques en disant qu'en général vous ne devriez pas l'appeler. Je pense que c'est un cas de "si vous devez demander si vous devez utiliser cela, vous ne devriez pas"
EDIT pour répondre à quelques préoccupations de l'autre thread:
Après avoir lu le fil que vous avez lié, il y a quelques autres choses que je voudrais souligner. Tout d'abord, quelqu'un a suggéré que les appels
gc()
pouvaient renvoyer de la mémoire au système. Ce n'est certainement pas nécessairement vrai - le tas Java lui-même se développe indépendamment des allocations Java.Comme dans, la JVM contiendra de la mémoire (plusieurs dizaines de mégaoctets) et augmentera le tas si nécessaire. Il ne renvoie pas nécessairement cette mémoire au système même lorsque vous libérez des objets Java; il est parfaitement libre de conserver la mémoire allouée à utiliser pour les futures allocations Java.
Pour montrer qu'il est possible que
System.gc()
cela ne fasse rien, consultez:http://bugs.sun.com/view_bug.do?bug_id=6668279
et en particulier qu'il existe une option -XX: DisableExplicitGC VM.
la source
System.gc()
sont utiles et pourraient même être nécessaires. Par exemple, dans les applications d'interface utilisateur sur Windows, cela peut considérablement accélérer le processus de restauration d'une fenêtre lorsque vous appelez System.gc () avant de minimiser la fenêtre (en particulier lorsqu'elle reste minimisée pendant un certain temps et que certaines parties du processus sont échangées vers disque).WeakReference
s pour les objets que vous souhaitez conserver est incorrect dès le début, la récupération de place ou non. Vous auriez le même problème en C ++ avecstd::weak_ptr
(bien que vous puissiez remarquer le problème dans une version C ++ plus tôt que dans une version Java car la destruction des objets ne serait pas différée comme la finalisation est généralement).System.gc()
corriger est une solution de contournement, pas une bonne pratique de codage.Il a déjà été expliqué que l'appel
system.gc()
peut ne rien faire et que tout code qui "a besoin" du garbage collector pour s'exécuter est rompu.Cependant, la raison pragmatique pour laquelle il est inapproprié d'appeler
System.gc()
est qu'elle est inefficace. Et dans le pire des cas, c'est horriblement inefficace ! Laisse-moi expliquer.Un algorithme GC typique identifie les ordures en parcourant tous les objets non-ordures du tas et en déduisant que tout objet non visité doit être une ordure. À partir de cela, nous pouvons modéliser le travail total d'une collecte des ordures se compose d'une partie qui est proportionnelle à la quantité de données en direct, et une autre partie qui est proportionnelle à la quantité de déchets; ie
work = (live * W1 + garbage * W2)
.Supposons maintenant que vous effectuez les opérations suivantes dans une application monothread.
Le premier appel fonctionnera (nous le prédisons)
(live * W1 + garbage * W2)
et éliminera les déchets en suspens.Le deuxième appel
(live* W1 + 0 * W2)
fonctionnera et ne récupérera rien. En d'autres termes, nous avons fait du(live * W1)
travail et n'avons absolument rien obtenu .Nous pouvons modéliser l'efficacité du collecteur comme la quantité de travail nécessaire pour collecter une unité de déchets; ie
efficiency = (live * W1 + garbage * W2) / garbage
. Donc, pour rendre le GC aussi efficace que possible, nous devons maximiser la valeur dugarbage
moment où nous exécutons le GC; c'est à dire attendre que le tas soit plein. (Et aussi, faites le tas le plus grand possible. Mais c'est un sujet distinct.)Si l'application n'interfère pas (en appelant
System.gc()
), le GC attendra que le segment de mémoire soit plein avant de s'exécuter, résultant en une collecte efficace des déchets 1 . Mais si l'application oblige le GC à s'exécuter, il est probable que le tas ne soit pas plein, et le résultat sera que les ordures seront collectées de manière inefficace. Et plus l'application force le GC, plus le GC devient inefficace.Remarque: l'explication ci-dessus passe sous silence le fait qu'un GC moderne typique partitionne le tas en "espaces", le GC peut développer dynamiquement le tas, l'ensemble de travail de l'application des objets non-ordures peut varier et ainsi de suite. Même ainsi, le même principe de base s'applique à tous les vrais récupérateurs 2 . Il est inefficace de forcer le GC à s'exécuter.
1 - Voici comment fonctionne le collecteur "débit". Les collecteurs simultanés tels que CMS et G1 utilisent différents critères pour décider quand démarrer le garbage collector.
2 - J'exclus également les gestionnaires de mémoire qui utilisent exclusivement le comptage de références, mais aucune implémentation Java actuelle n'utilise cette approche ... pour une bonne raison.
la source
Beaucoup de gens semblent vous dire de ne pas faire ça. Je ne suis pas d'accord. Si, après un processus de chargement important comme le chargement d'un niveau, vous pensez que:
il n'y a aucun mal à appeler System.gc (). Je le regarde comme le
inline
mot-clé c / c ++ . C'est juste un indice pour le GC que vous, le développeur, avez décidé que le temps / les performances ne sont pas aussi importants qu'ils le sont habituellement et que certains pourraient être utilisés pour récupérer de la mémoire.Le conseil de ne pas compter sur lui pour faire quoi que ce soit est correct. Ne comptez pas sur son fonctionnement, mais donner l'indication que c'est maintenant un moment acceptable pour collecter est parfaitement bien. Je préfère perdre du temps à un point du code où cela n'a pas d'importance (écran de chargement) plutôt que lorsque l'utilisateur interagit activement avec le programme (comme lors d'un niveau de jeu.)
Il y a une fois où je vais forcer la collecte: lorsque j'essaie de trouver un objet particulier, il y a des fuites (code natif ou grande interaction de rappel complexe. Oh et tout composant d'interface utilisateur qui ressemble à Matlab.) Cela ne devrait jamais être utilisé dans le code de production.
la source
stop the world
approche et c'est un vrai mal, si cela se produitLes gens ont fait du bon travail en expliquant pourquoi NE PAS utiliser, donc je vais vous dire quelques situations où vous devriez l'utiliser:
(Les commentaires suivants s'appliquent au Hotspot fonctionnant sous Linux avec le collecteur CMS, où je suis sûr de dire que cela
System.gc()
appelle en fait toujours un ramasse-miettes complet).Après le travail initial de démarrage de votre application, vous pouvez être dans un état d'utilisation de la mémoire terrible. La moitié de votre génération occupée pourrait être pleine d'ordures, ce qui signifie que vous êtes beaucoup plus proche de votre premier CMS. Dans les applications où cela importe, ce n'est pas une mauvaise idée d'appeler System.gc () pour "réinitialiser" votre tas à l'état de départ des données en direct.
Dans le même esprit que # 1, si vous surveillez de près votre utilisation du segment de mémoire, vous voulez avoir une lecture précise de votre utilisation de la mémoire de base. Si les 2 premières minutes de disponibilité de votre application sont toutes initialisées, vos données vont être gâchées, sauf si vous forcez (ahem ... "suggérer") le gc complet à l'avance.
Vous pouvez avoir une application conçue pour ne jamais promouvoir quoi que ce soit auprès de la génération permanente pendant son fonctionnement. Mais vous devez peut-être initialiser certaines données à l'avance qui ne sont pas si énormes que pour passer automatiquement à la génération permanente. Sauf si vous appelez System.gc () une fois que tout est configuré, vos données peuvent rester dans la nouvelle génération jusqu'à ce que le moment soit venu de les promouvoir. Tout à coup, votre application à faible latence et faible GC super-duper est frappée d'une pénalité de latence ÉNORME (relativement parlant, bien sûr) pour la promotion de ces objets pendant les opérations normales.
Il est parfois utile d'avoir un appel System.gc disponible dans une application de production pour vérifier l'existence d'une fuite de mémoire. Si vous savez que l'ensemble des données en direct au moment X doit exister dans un certain rapport avec l'ensemble des données en direct au moment Y, il peut être utile d'appeler System.gc () un temps X et un temps Y et de comparer l'utilisation de la mémoire .
la source
System.gc()
une fois pour forcer la promotion des objets ne vous rapporte rien. Et vous ne voulez certainement pas appelerSystem.gc()
huit fois de suite et prier pour que maintenant, la promotion soit terminée et que les coûts économisés d'une promotion ultérieure justifient les coûts de plusieurs GC complets. Selon l'algorithme GC, la promotion d'un grand nombre d'objets peut même ne pas supporter les coûts réels, car cela ne fera que réaffecter la mémoire à l'ancienne génération ou copier simultanément…C'est une question très gênante, et je pense que beaucoup de gens s'opposent à Java malgré l'utilité d'un langage.
Le fait que vous ne puissiez pas faire confiance à «System.gc» pour faire quoi que ce soit est incroyablement intimidant et peut facilement invoquer la sensation «Peur, incertitude, doute» du langage.
Dans de nombreux cas, il est agréable de traiter les pics de mémoire que vous causez exprès avant qu'un événement important ne se produise, ce qui inciterait les utilisateurs à penser que votre programme est mal conçu / ne répond pas.
Avoir la capacité de contrôler la collecte des ordures serait un très bon outil d'éducation, améliorant à son tour la compréhension du fonctionnement de la collecte des ordures et la façon dont les programmes exploitent son comportement par défaut ainsi que son comportement contrôlé.
Permettez-moi de passer en revue les arguments de ce fil.
Souvent, le programme ne fait rien et vous savez qu'il ne fait rien à cause de la façon dont il a été conçu. Par exemple, il peut faire une longue attente avec une grande boîte de message d'attente, et à la fin, il peut également ajouter un appel pour collecter les ordures, car le temps de l'exécuter prendra une très petite fraction du temps de la longue attente, mais cela évitera à gc d'agir au milieu d'une opération plus importante.
Je ne suis pas d'accord, peu importe ce que vous avez à ramasser. Son travail consiste à suivre les déchets et à les nettoyer.
En appelant le gc pendant les périodes où l'utilisation est moins critique, vous réduisez les chances qu'il s'exécute lorsque votre vie dépend du code spécifique en cours d'exécution mais qu'il décide plutôt de collecter les ordures.
Bien sûr, il ne se comportera peut-être pas comme vous le souhaitez ou attendez, mais lorsque vous voulez l'appeler, vous savez que rien ne se passe et l'utilisateur est prêt à tolérer la lenteur / les temps d'arrêt. Si le System.gc fonctionne, tant mieux! Si ce n'est pas le cas, au moins vous avez essayé. Il n'y a tout simplement pas d'inconvénient, sauf si le garbage collector a des effets secondaires inhérents qui font quelque chose d'horriblement inattendu à la façon dont un garbage collector est censé se comporter s'il est appelé manuellement, et cela en soi provoque de la méfiance.
Il s'agit d'un cas d'utilisation qui ne peut pas être atteint de manière fiable, mais pourrait l'être si le système était conçu de cette façon. C'est comme faire un feu de signalisation et faire en sorte que certains / tous les boutons des feux de signalisation ne fassent rien, cela vous fait vous demander pourquoi le bouton est là pour commencer, javascript n'a pas de fonction de collecte des ordures, donc nous ne pas le scruter autant pour elle.
qu'est-ce qu'un "indice"? qu'est-ce que "ignorer"? un ordinateur ne peut pas simplement prendre des indices ou ignorer quelque chose, il existe des chemins de comportement stricts qui peuvent être dynamiques et qui sont guidés par l'intention du système. Une bonne réponse inclurait ce que le garbage collector fait réellement, au niveau de l'implémentation, qui l'empêche d'effectuer la collecte lorsque vous le demandez. La fonctionnalité est-elle simplement un nop? Y a-t-il une sorte de conditions que je dois remplir? Quelles sont ces conditions?
En l'état, le GC de Java ressemble souvent à un monstre auquel vous ne faites pas confiance. Vous ne savez pas quand ça va venir ou partir, vous ne savez pas ce que ça va faire, comment ça va le faire. Je peux imaginer que certains experts aient une meilleure idée du fonctionnement de leur ramasse-miettes par instruction, mais la grande majorité espère simplement que cela "fonctionne" et il est frustrant de faire confiance à un algorithme d'apparence opaque pour faire le travail à votre place.
Il y a un grand écart entre lire quelque chose ou apprendre quelque chose, et en fait voir l'implémentation de celui-ci, les différences entre les systèmes, et être capable de jouer avec sans avoir à regarder le code source. Cela crée de la confiance et un sentiment de maîtrise / compréhension / contrôle.
Pour résumer, il y a un problème inhérent avec les réponses "cette fonctionnalité pourrait ne rien faire, et je n'entrerai pas dans les détails comment dire quand elle fait quelque chose et quand elle ne le fait pas et pourquoi elle ne le fera pas ou le fera, impliquant souvent qu'il est tout simplement contraire à la philosophie d'essayer de le faire, même si l'intention derrière cela est raisonnable ".
Il peut être correct pour Java GC de se comporter comme il le fait, ou non, mais pour le comprendre, il est difficile de vraiment suivre dans quelle direction aller pour obtenir un aperçu complet de ce que vous pouvez faire confiance au GC et ne pas faire, il est donc trop facile de se méfier simplement de la langue, car le but d'une langue est d'avoir un comportement contrôlé jusqu'à une mesure philosophique (il est facile pour un programmeur, en particulier les novices, de tomber dans une crise existentielle de certains comportements système / langage) vous sont capables de tolérer (et si vous ne le pouvez pas, vous n'utiliserez pas la langue tant que vous ne le devrez pas), et plus de choses que vous ne pouvez pas contrôler sans raison connue pour laquelle vous ne pouvez pas les contrôler est intrinsèquement nuisible.
la source
L'efficacité du GC repose sur un certain nombre d'heuristiques. Par exemple, une heuristique courante est que les accès en écriture aux objets se produisent généralement sur des objets qui ont été créés il n'y a pas longtemps. Un autre est que de nombreux objets ont une durée de vie très courte (certains objets seront utilisés pendant longtemps, mais beaucoup seront jetés quelques microsecondes après leur création).
Appeler,
System.gc()
c'est comme donner un coup de pied au GC. Cela signifie: "tous ces paramètres soigneusement réglés, ces organisations intelligentes, tous les efforts que vous venez de consacrer à l'allocation et à la gestion des objets de telle sorte que les choses se passent bien, eh bien, abandonnez le tout et recommencez à zéro". Il peut améliorer les performances, mais la plupart du temps, il ne fait que dégrader les performances.Pour utiliser de
System.gc()
manière fiable (*), vous devez savoir comment fonctionne le GC dans tous ses détails. Ces détails ont tendance à changer un peu si vous utilisez une JVM d'un autre fournisseur, ou la prochaine version du même fournisseur, ou la même JVM mais avec des options de ligne de commande légèrement différentes. C'est donc rarement une bonne idée, sauf si vous souhaitez résoudre un problème spécifique dans lequel vous contrôlez tous ces paramètres. D'où la notion de «mauvaise pratique»: ce n'est pas interdit, la méthode existe, mais elle est rarement payante.(*) Je parle ici d'efficacité.
System.gc()
ne cassera jamais un programme Java correct. Cela ne provoquera pas non plus de mémoire supplémentaire que la JVM n'aurait pas pu obtenir autrement: avant de lancer uneOutOfMemoryError
, la JVM fait le travailSystem.gc()
, même en dernier recours.la source
Parfois ( pas souvent! ), Vous en savez vraiment plus sur l'utilisation de la mémoire passée, actuelle et future, puis sur le temps d'exécution. Cela n'arrive pas très souvent, et je dirais que jamais dans une application Web pendant que des pages normales sont servies.
Il y a plusieurs années, je travaille sur un générateur de rapports,
Tout d'abord, comme ce n'était pas en temps réel et que les utilisateurs s'attendaient à attendre un rapport, un retard pendant l'exécution du GC n'était pas un problème, mais nous devions produire des rapports à un rythme plus rapide que prévu.
En regardant le schéma ci-dessus du processus, il est clair que.
Par conséquent, il valait clairement la peine de faire une exécution GC chaque fois que la file d'attente des demandes était vide; il n'y avait aucun inconvénient à cela.
Il peut être utile de faire une analyse GC après l'envoi de chaque rapport, car nous savons que c'est le bon moment pour une analyse GC. Cependant, si l'ordinateur avait suffisamment de RAM, de meilleurs résultats seraient obtenus en retardant l'exécution du GC.
Ce comportement a été configuré sur une base par installation, pour certains clients permettant un GC forcé après chaque rapport, ce qui a considérablement accéléré la protection des rapports. (Je m'attends à ce que cela soit dû à une mémoire insuffisante sur leur serveur et à l'exécution de nombreux autres processus, donc un GC forcé bien réduit a réduit la pagination.)
Nous n'avons jamais détecté une installation qui ne bénéficiait pas d'une exécution forcée du GC à chaque fois que la file d'attente de travail était vide.
Mais, soyons clairs, ce qui précède n'est pas un cas courant.
la source
J'écris peut-être du code merdique, mais je me suis rendu compte que cliquer sur l'icône de la corbeille sur les IDE d'éclipse et de netbeans est une «bonne pratique».
la source
System.gc()
périodiquement, vous trouveriez probablement le comportement ennuyeux.Premièrement, il existe une différence entre la spécification et la réalité. La spécification indique que System.gc () est un indice que GC devrait exécuter et que la machine virtuelle est libre de l'ignorer. La réalité est que la VM n'ignorera jamais un appel à System.gc ().
L'appel de GC est livré avec un surcoût non trivial à l'appel et si vous le faites à un moment aléatoire, il est probable que vous ne verrez aucune récompense pour vos efforts. D'un autre côté, une collecte déclenchée naturellement est très susceptible de récupérer les coûts de l'appel. Si vous avez des informations qui indiquent qu'un GC doit être exécuté, vous pouvez appeler System.gc () et vous devriez voir les avantages. Cependant, d'après mon expérience, cela ne se produit que dans quelques cas marginaux car il est très peu probable que vous disposiez de suffisamment d'informations pour comprendre si et quand System.gc () doit être appelé.
Un exemple répertorié ici, frapper la poubelle dans votre IDE. Si vous vous rendez à une réunion, pourquoi ne pas la rencontrer? Les frais généraux ne vous affecteront pas et le tas pourrait être nettoyé lorsque vous reviendrez. Faites-le dans un système de production et les appels fréquents à la collecte l'arrêteront brusquement! Même les appels occasionnels tels que ceux effectués par RMI peuvent perturber les performances.
la source
Oui, appeler System.gc () ne garantit pas qu'il fonctionnera, c'est une demande à la JVM qui peut être ignorée. De la documentation:
C'est presque toujours une mauvaise idée de l'appeler car la gestion automatique de la mémoire sait généralement mieux que vous quand gc. Il le fera lorsque son pool interne de mémoire libre est faible, ou si le système d'exploitation demande de récupérer de la mémoire.
Il peut être acceptable d'appeler System.gc () si vous savez que cela aide. J'entends par là que vous avez soigneusement testé et mesuré le comportement des deux scénarios sur la plate - forme de déploiement , et vous pouvez montrer que cela aide. Sachez cependant que le gc n'est pas facilement prévisible - il peut aider lors d'une course et blesser une autre.
la source
D'après mon expérience, l'utilisation de System.gc () est en fait une forme d'optimisation spécifique à la plate-forme (où "plate-forme" est la combinaison de l'architecture matérielle, du système d'exploitation, de la version JVM et éventuellement d'autres paramètres d'exécution tels que la RAM disponible), car son comportement, bien que plus ou moins prévisible sur une plate-forme spécifique, elle peut (et variera) considérablement entre les plates-formes.
Oui, il existe des situations où System.gc () améliorera les performances (perçues). Par exemple, si les retards sont tolérables dans certaines parties de votre application, mais pas dans d'autres (l'exemple de jeu cité ci-dessus, où vous voulez que GC se produise au début d'un niveau, pas pendant le niveau).
Cependant, que cela aide ou blesse (ou ne fasse rien) est très dépend de la plate-forme (comme défini ci-dessus).
Je pense donc que c'est valable en tant qu'optimisation spécifique à la plate-forme de dernier recours (c'est-à-dire si d'autres optimisations de performances ne suffisent pas). Mais vous ne devriez jamais l'appeler simplement parce que vous pensez que cela pourrait aider (sans repères spécifiques), car il est probable que ce ne sera pas le cas.
la source
Étant donné que les objets sont alloués dynamiquement à l'aide du nouvel opérateur,
vous vous demandez peut-être comment ces objets sont détruits et leur
mémoire libérée pour une réallocation ultérieure.
Dans certains langages, comme C ++, les objets alloués dynamiquement doivent être libérés manuellement à l'aide d'un opérateur de suppression.
la source