Pourquoi est-ce une mauvaise pratique d'appeler System.gc ()?

326

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?
zneak
la source
4
Je pense que le bon moment pour appeler System.gc () est lorsque vous faites déjà quelque chose qui est un long processus de chargement. Par exemple, je travaille sur un jeu et je prévois d'appeler System.gc () lorsque le jeu chargera un nouveau niveau, à la fin du processus de chargement. L'utilisateur attend déjà un peu, et l'augmentation des performances supplémentaires en vaut la peine; mais je placerai également une option dans l'écran de configuration pour désactiver ce comportement.
Ricket
41
Bozho: notez le premier mot de la question. POURQUOI est-il préférable de ne pas l'appeler? La simple répétition d'un mantra n'explique rien.
JUSTE MON AVIS correct
1
Un autre cas a été expliqué dans java-monitor.com/forum/showthread.php?t=188 où il explique comment il peut être une mauvaise pratique d'appeler System.gc ()
Shirishkumar Bari

Réponses:

243

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.

Steven Schlansker
la source
1
Vous pourrez peut-être créer une configuration étrange Rube Goldberg-esque où la méthode dans laquelle le GC est exécuté affecte l'exactitude de votre code. Peut-être que cela masque une interaction de threading étrange, ou peut-être qu'un finaliseur a un effet significatif sur le fonctionnement du programme. Je ne suis pas tout à fait sûr que ce soit possible, mais c'est possible, alors j'ai pensé que je le mentionnerais.
Steven Schlansker
2
@zneak vous pourriez par exemple avoir mis du code critique dans les finaliseurs (qui est un code fondamentalement cassé)
Martin
3
Je voudrais ajouter qu'il y a quelques cas de coin qui 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).
Joachim Sauer
2
@AndrewJanke Je dirais que le code qui utilise WeakReferences 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 ++ avec std::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).
JAB
2
@rebeccah C'est un bug, alors oui, je l'appellerais "certainement cassé". Le fait de le System.gc()corriger est une solution de contournement, pas une bonne pratique de codage.
Steven Schlansker
149

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.

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

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 du garbagemoment 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.

Stephen C
la source
45
+1 Bonne explication. Notez cependant que ce raisonnement ne s'applique que si vous vous souciez du débit. Si vous souhaitez optimiser la latence à des points spécifiques, forcer GC peut être logique. Par exemple (hypothétiquement parlant) dans un jeu, vous voudrez peut-être éviter les retards pendant les niveaux, mais vous ne vous souciez pas des retards pendant le chargement de niveau. Il serait alors judicieux de forcer GC après une charge de niveau. Cela diminue le débit global, mais ce n'est pas ce que vous optimisez.
sleske
2
@sleske - ce que vous dites est vrai. Cependant, l'exécution du GC entre les niveaux est une solution de pansement ... et ne résout pas le problème de latence si les niveaux prennent suffisamment de temps pour que vous ayez besoin d'exécuter le GC pendant un niveau de toute façon. Une meilleure approche consiste à utiliser un ramasse-miettes simultané (pause faible) ... si la plate-forme le prend en charge.
Stephen C
Je ne suis pas tout à fait sûr de ce que vous entendez par "a besoin du ramasse-miettes pour fonctionner". Les conditions que les applications doivent éviter sont: échecs d'allocation qui ne peuvent jamais être satisfaits et frais généraux élevés du GC. Des appels aléatoires à System.gc () entraînent souvent une surcharge de GC élevée.
Kirk
@Kirk - "Faire des appels au hasard à System.gc () entraîne souvent une surcharge GC élevée." . Je le sais. Il en serait de même pour quiconque ayant lu et compris ma réponse.
Stephen C
@Kirk - " Je ne suis pas tout à fait sûr de ce que vous voulez dire ..." - Je veux dire des programmes où le comportement "correct" d'un programme dépend de l'exécution du GC à un moment particulier; par exemple afin d'exécuter des finaliseurs, ou de casser des références faibles ou des références souples. C'est vraiment une mauvaise idée de faire ça ... mais c'est de ça que je parle. Voir également la réponse de Steven Schlansker.
Stephen C
47

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:

  1. Vous avez beaucoup d'objets inaccessibles et n'ont peut-être pas été détectés. et
  2. Vous pensez que l'utilisateur pourrait supporter un petit ralentissement à ce stade

il n'y a aucun mal à appeler System.gc (). Je le regarde comme le inlinemot-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.

KitsuneYMG
la source
3
+1 pour GC lors de l'analyse des fuites mem. Notez que les informations sur l'utilisation du tas (Runtime.freeMemory () et al.) Ne sont vraiment significatives qu'après avoir forcé un GC, sinon cela dépendrait de la dernière fois où le système a pris la peine d'exécuter un GC.
sleske
4
il n'y a aucun mal à appeler System.gc (), cela pourrait nécessiter une stop the worldapproche et c'est un vrai mal, si cela se produit
bestsss
7
Il y a un mal à appeler explicitement le garbage collector. Appeler le GC au mauvais moment gaspille les cycles CPU. Vous (le programmeur) n'avez pas suffisamment d'informations pour déterminer le moment opportun ... mais la JVM en a.
Stephen C
Jetty fait 2 appels à System.gc () après le démarrage et j'ai rarement vu que cela faisait une différence.
Kirk
Eh bien, les développeurs de Jetty ont un bug. Cela ferait une différence ... même si la différence est difficile à quantifier.
Stephen C
31

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

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

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

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

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

JT.
la source
1
Pour la plupart des récupérateurs de génération, les objets de la nouvelle génération doivent survivre à un certain nombre (souvent configurable) de récupérateurs, donc appeler System.gc()une fois pour forcer la promotion des objets ne vous rapporte rien. Et vous ne voulez certainement pas appeler System.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…
Holger
11

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.

  1. C'est inefficace:

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.

  1. C'est toujours une mauvaise pratique et indique un code cassé.

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.

  1. Ce n'est pas un cas d'utilisation courant:

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.

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

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.

Dmitry
la source
9

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 une OutOfMemoryError, la JVM fait le travail System.gc(), même en dernier recours.

Thomas Pornin
la source
1
+1 pour avoir mentionné que System.gc () n'empêche pas OutOfMemoryError. Certaines personnes le croient.
sleske
1
En fait, cela peut empêcher OutOfMemoryError en raison de la gestion des références logicielles. Les SoftReferences créées après la dernière exécution du GC ne sont pas collectées dans l'implémentation que je connais. Mais ce détail d'implémentation est sujet à changement à tout moment et une sorte de bogue et rien sur quoi vous devriez compter.
maaartinus
8

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,

  • Avait un seul fil
  • Lire la «demande de rapport» dans une file d'attente
  • A chargé les données nécessaires pour le rapport à partir de la base de données
  • Généré le rapport et envoyé par e-mail.
  • Répété pour toujours, dormant quand il n'y avait aucune demande en suspens.
  • Il n'a réutilisé aucune donnée entre les rapports et n'a effectué aucun encaissement.

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.

  • Nous savons qu'il y aurait très peu d'objets vivants juste après l'envoi d'un rapport, car la prochaine demande n'avait pas encore commencé à être traitée.
  • Il est bien connu que le coût d'exécution d'un cycle de collecte des ordures dépend du nombre d'objets vivants, la quantité d'ordures a peu d'effet sur le coût d'une exécution GC.
  • Que lorsque la file d'attente est vide, il n'y a rien de mieux à faire que d'exécuter le GC.

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.

Ian Ringrose
la source
3

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

Ryan Fernandes
la source
1
C'est peut-être le cas. Mais si Eclipse ou NetBeans était programmé pour appeler System.gc()périodiquement, vous trouveriez probablement le comportement ennuyeux.
Stephen C
2

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.

Église
la source
3
"La réalité est que la VM n'ignorera jamais un appel à System.gc ()." - Incorrect. En savoir plus sur -XX: -DisableExplicitGC.
Stephen C
1

Oui, appeler System.gc () ne garantit pas qu'il fonctionnera, c'est une demande à la JVM qui peut être ignorée. De la documentation:

L'appel de la méthode gc suggère que la machine virtuelle Java consacre des efforts au recyclage des objets inutilisés

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.

à M
la source
<stroke> Mais aussi à partir de Javadoc: _Lorsque le contrôle revient de l'appel de méthode, la machine virtuelle a fait de son mieux pour recycler tous les objets rejetés, ce que je considère comme une forme plus impérative de ce que vous avez publié. </ stroke > Vissez ça, il y a un rapport de bug sur le fait qu'il est trompeur. Comme qui sait mieux, quels sont les inconvénients de faire allusion à la JVM?
zneak
1
Le mal est que faire la collecte au mauvais moment peut être un énorme ralentissement. L'indice que vous donnez est probablement mauvais. Quant au commentaire "best effort", essayez-le et voyez dans un outil comme JConsole. Parfois, cliquer sur le bouton "Exécuter GC" ne fait rien
tom
Désolé d'être en désaccord, mais appeler System.gc () dans OpenJDK et tout ce qui en découle (HP par exemple) entraîne toujours un cycle de récupération de place. En fait, cela semble également vrai pour la mise en œuvre J9 d'IBM
Kirk
@Kirk - Incorrect: google, et lisez-en plus sur -XX: -DisableExplicitGC.
Stephen C
0

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.

sleske
la source
0
  1. É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.

  2. Dans certains langages, comme C ++, les objets alloués dynamiquement doivent être libérés manuellement à l'aide d'un opérateur de suppression.

  3. Java adopte une approche différente; il gère automatiquement la désallocation.
  4. La technique qui accomplit cela s'appelle la collecte des ordures. Cela fonctionne comme ceci: lorsqu'il n'existe aucune référence à un objet, cet objet est supposé ne plus être nécessaire et la mémoire occupée par l'objet peut être récupérée. Il n'y a pas de besoin explicite de détruire des objets comme en C ++.
  5. La récupération de place ne se produit que sporadiquement (voire pas du tout) pendant l'exécution de votre programme.
  6. Cela ne se produira pas simplement parce qu'il existe un ou plusieurs objets qui ne sont plus utilisés.
  7. De plus, différentes implémentations d'exécution Java adopteront différentes approches de la récupération de place, mais pour la plupart, vous ne devriez pas avoir à y penser lors de l'écriture de vos programmes.
Tanmay Delhikar
la source