Java Language Piège - L'appel de System.gc () est inefficace


Exemple

C'est (presque toujours) une mauvaise idée d'appeler System.gc() .

Le javadoc pour la méthode gc() spécifie ce qui suit:

"L'appel de la méthode gc suggère que la machine virtuelle Java déploie des efforts pour recycler les objets inutilisés afin de pouvoir réutiliser rapidement la mémoire qu'elle occupe actuellement. Lorsque le contrôle revient de l'appel de méthode, la machine virtuelle Java s'efforce de récupérer espace de tous les objets jetés. "

On peut en tirer quelques points importants:

  1. L'utilisation du mot "suggère" plutôt que (dis) "raconte" signifie que la JVM est libre d'ignorer la suggestion. Le comportement par défaut de la JVM (versions récentes) doit suivre la suggestion, mais cela peut être remplacé par la définition de -XX:+DisableExplicitGC lors du lancement de la JVM.

  2. La phrase "un meilleur effort pour récupérer de l'espace à partir de tous les objets ignorés" implique que l'appel de gc déclenchera une récupération de gc "complète".

Alors pourquoi appeler System.gc() une mauvaise idée?

Tout d'abord, exécuter une collecte de place complète est coûteux. Un GC complet implique la visite et le "marquage" de chaque objet encore accessible; c'est-à-dire chaque objet qui n'est pas une poubelle. Si vous déclenchez cela alors qu'il n'y a pas beaucoup de déchets à collecter, le GC fait beaucoup de travail pour relativement peu d'avantages.

Deuxièmement, une récupération de place complète risque de perturber les propriétés de "localité" des objets non collectés. Les objets alloués par le même thread à peu près au même moment ont tendance à être alloués de manière rapprochée en mémoire. C'est bon. Les objets alloués en même temps sont susceptibles d'être liés; c'est-à-dire se référencer Si votre application utilise ces références, il est probable que l’accès à la mémoire sera plus rapide en raison des divers effets de mémoire et de mise en cache des pages. Malheureusement, une récupération de mémoire complète a tendance à déplacer des objets, de sorte que les objets qui étaient autrefois fermés sont désormais plus éloignés.

Troisièmement, l'exécution d'une récupération de place complète risque de mettre votre application en pause jusqu'à ce que la collecte soit terminée. Pendant que cela se produit, votre demande ne sera pas recevable.

En fait, la meilleure stratégie consiste à laisser la JVM décider du moment où exécuter le GC et du type de collection à exécuter. Si vous n'intervenez pas, la machine virtuelle Java choisira un type de temps et de collection qui optimise le débit ou minimise les temps de pause du GC.


Au début, nous avons dit "(presque toujours) une mauvaise idée ...". En fait, il existe quelques scénarios dans lesquels cela pourrait être une bonne idée:

  1. Si vous implémentez un test unitaire pour un code sensible au ramassage des ordures (par exemple, quelque chose impliquant des finaliseurs ou des références faibles / douces / fantômes), il peut être nécessaire d'appeler System.gc() .

  2. Dans certaines applications interactives, il peut y avoir des moments particuliers où l'utilisateur ne se soucie pas de savoir s'il y a une pause de récupération de place. Un exemple est un jeu où il y a des pauses naturelles dans le "jeu"; par exemple lors du chargement d'un nouveau niveau.