Aujourd'hui, mes collègues et moi avons une discussion sur l'utilisation du final
mot - clé en Java pour améliorer le ramasse-miettes.
Par exemple, si vous écrivez une méthode comme:
public Double doCalc(final Double value)
{
final Double maxWeight = 1000.0;
final Double totalWeight = maxWeight * value;
return totalWeight;
}
La déclaration des variables dans la méthode final
aiderait le garbage collection à nettoyer la mémoire des variables inutilisées dans la méthode après la fin de la méthode.
Est-ce vrai?
java
garbage-collection
final
Goran Martinic
la source
la source
Réponses:
Voici un exemple légèrement différent, un avec des champs de type référence finaux plutôt que des variables locales de type valeur finale:
public class MyClass { public final MyOtherObject obj; }
Chaque fois que vous créez une instance de MyClass, vous créez une référence sortante à une instance de MyOtherObject, et le GC devra suivre ce lien pour rechercher des objets en direct.
La JVM utilise un algorithme GC mark-sweep, qui doit examiner toutes les références en direct dans les emplacements GC "racine" (comme tous les objets de la pile d'appels actuelle). Chaque objet vivant est «marqué» comme vivant, et tout objet auquel un objet vivant fait référence est également marqué comme vivant.
Une fois la phase de marquage terminée, le GC parcourt le tas, libérant de la mémoire pour tous les objets non marqués (et compactant la mémoire pour les objets vivants restants).
En outre, il est important de reconnaître que la mémoire de tas Java est partitionnée en une «jeune génération» et une «ancienne génération». Tous les objets sont initialement attribués à la jeune génération (parfois appelée «la crèche»). Comme la plupart des objets sont de courte durée, le GC est plus agressif pour libérer les déchets récents de la jeune génération. Si un objet survit à un cycle de collecte de la jeune génération, il est déplacé dans l'ancienne génération (parfois appelée «génération titulaire»), qui est traitée moins fréquemment.
Donc, du haut de ma tête, je vais dire "non, le modificateur 'final' n'aide pas le GC à réduire sa charge de travail".
À mon avis, la meilleure stratégie pour optimiser votre gestion de la mémoire en Java est d'éliminer les fausses références le plus rapidement possible. Vous pouvez le faire en attribuant «null» à une référence d'objet dès que vous avez fini de l'utiliser.
Ou, mieux encore, minimisez la taille de chaque portée de déclaration. Par exemple, si vous déclarez un objet au début d'une méthode de 1000 lignes, et si l'objet reste en vie jusqu'à la fermeture de la portée de cette méthode (la dernière accolade fermante), alors l'objet peut rester en vie beaucoup plus longtemps qu'en fait nécessaire.
Si vous utilisez de petites méthodes, avec seulement une douzaine de lignes de code, alors les objets déclarés dans cette méthode tomberont plus rapidement hors de portée, et le GC sera en mesure de faire l'essentiel de son travail dans un environnement beaucoup plus efficace. jeune génération. Vous ne voulez pas que les objets soient déplacés vers l'ancienne génération à moins que cela ne soit absolument nécessaire.
la source
La déclaration d'une variable locale
final
n'affectera pas le garbage collection, cela signifie seulement que vous ne pouvez pas modifier la variable. Votre exemple ci-dessus ne doit pas être compilé car vous modifiez la variabletotalWeight
qui a été marquéefinal
. D'un autre côté, déclarer une primitive (double
au lieu deDouble
)final
permettra à cette variable d'être incorporée dans le code appelant, ce qui pourrait entraîner une amélioration de la mémoire et des performances. Ceci est utilisé lorsque vous en avez plusieurspublic static final Strings
dans une classe.En général, le compilateur et le runtime optimiseront là où ils le peuvent. Il est préférable d'écrire le code de manière appropriée et de ne pas essayer d'être trop délicat. À utiliser
final
lorsque vous ne souhaitez pas que la variable soit modifiée. Supposons que toutes les optimisations faciles seront effectuées par le compilateur, et si vous êtes préoccupé par les performances ou l'utilisation de la mémoire, utilisez un profileur pour déterminer le problème réel.la source
Non, ce n'est absolument pas vrai.
N'oubliez pas que
final
cela ne signifie pas constant, cela signifie simplement que vous ne pouvez pas modifier la référence.final MyObject o = new MyObject(); o.setValue("foo"); // Works just fine o = new MyObject(); // Doesn't work.
Il peut y avoir une petite optimisation basée sur la connaissance que la JVM n'aura jamais à modifier la référence (comme ne pas avoir vérifié pour voir si elle a changé) mais ce serait si mineur que de ne pas s'inquiéter.
Final
devraient être considérées comme des métadonnées utiles pour le développeur et non comme une optimisation du compilateur.la source
Quelques points à éclaircir:
L'annulation de la référence ne devrait pas aider GC. Si c'est le cas, cela indiquerait que vos variables sont surdimensionnées. Une exception est le cas du népotisme d'objet.
Il n'y a pas encore d'allocation sur pile en Java.
Déclarer une variable final signifie que vous ne pouvez pas (dans des conditions normales) attribuer une nouvelle valeur à cette variable. Puisque final ne dit rien sur la portée, il ne dit rien sur son effet sur GC.
la source
Eh bien, je ne connais pas l'utilisation du modificateur "final" dans ce cas, ni son effet sur le GC.
Mais je peux vous dire ceci: votre utilisation de valeurs Boxed plutôt que de primitives (par exemple, Double au lieu de double) allouera ces objets sur le tas plutôt que sur la pile, et produira des déchets inutiles que le GC devra nettoyer.
J'utilise uniquement des primitives encadrées lorsque cela est requis par une API existante, ou lorsque j'ai besoin de primitives Nullables.
la source
Les variables finales ne peuvent pas être modifiées après l'affectation initiale (imposée par le compilateur).
Cela ne modifie pas le comportement du garbage collection en tant que tel. La seule chose est que ces variables ne peuvent pas être annulées lorsqu'elles ne sont plus utilisées (ce qui peut aider le garbage collection dans des situations de mémoire serrée).
Vous devez savoir que final permet au compilateur de faire des hypothèses sur ce qu'il faut optimiser. Code incorporé et non compris le code connu pour ne pas être accessible.
final boolean debug = false; ...... if (debug) { System.out.println("DEBUG INFO!"); }
Le println ne sera pas inclus dans le code d'octet.
la source
Il existe un cas de coin moins connu avec les ramasseurs d'ordures générationnels. (Pour une brève description, lisez la réponse de benjismith pour un aperçu plus approfondi, lisez les articles à la fin).
L'idée dans les CG générationnels est que la plupart du temps, seules les jeunes générations doivent être prises en compte. L'emplacement racine est scanné pour les références, puis les objets de la jeune génération sont scannés. Lors de ces balayages plus fréquents, aucun objet de l'ancienne génération n'est vérifié.
Maintenant, le problème vient du fait qu'un objet n'est pas autorisé à avoir des références à des objets plus jeunes. Lorsqu'un objet de longue durée (ancienne génération) obtient une référence à un nouvel objet, cette référence doit être explicitement suivie par le garbage collector (voir l'article d'IBM sur le collecteur JVM hotspot ), affectant en fait les performances du GC.
La raison pour laquelle un ancien objet ne peut pas faire référence à un objet plus jeune est que, comme l'ancien objet n'est pas vérifié dans les collections mineures, si la seule référence à l'objet est conservée dans l'ancien objet, il ne sera pas marqué et serait à tort désalloué pendant la phase de balayage.
Bien sûr, comme beaucoup l'ont souligné, le mot-clé final n'affecte pas vraiment le ramasse-miettes, mais il garantit que la référence ne sera jamais transformée en un objet plus jeune si cet objet survit aux collections mineures et parvient à l'ancien tas.
Des articles:
IBM sur le garbage collection: historique , dans la JVM hotspot et performances . Celles-ci ne sont peut-être plus entièrement valides, car elles remontent à 2003/04, mais elles donnent un aperçu facile à lire des CG.
Sun on Tuning garbage collection
la source
GC agit sur les références inaccessibles. Cela n'a rien à voir avec le terme «final», qui est simplement une affirmation de cession unique. Est-il possible que certains GC de VM puissent utiliser "final"? Je ne vois ni comment ni pourquoi.
la source
final
sur les variables et les paramètres locaux ne fait aucune différence pour les fichiers de classe produits, donc ne peut pas affecter les performances d'exécution. Si une classe n'a pas de sous-classes, HotSpot traite cette classe comme si elle était finale de toute façon (il peut annuler plus tard si une classe qui rompt cette hypothèse est chargée). Je crois quefinal
les méthodes sont sensiblement les mêmes que les classes.final
sur un champ statique peut permettre à la variable d'être interprétée comme une "constante de compilation" et l'optimisation doit être effectuée par javac sur cette base.final
sur les champs permet à la JVM une certaine liberté d'ignorer les relations qui se produisent avant .la source
Il semble y avoir beaucoup de réponses qui sont des conjectures errantes. La vérité est qu'il n'y a pas de modificateur final pour les variables locales au niveau du bytecode. La machine virtuelle ne saura jamais que vos variables locales ont été définies comme finales ou non.
La réponse à votre question est un non catégorique.
la source
final int x; if (cond) x=1; else x=2;
). Le compilateur, sans le mot clé final, peut donc garantir qu'une variable est définitive ou non.Toutes les méthodes et variables peuvent être remplacées par défaut dans les sous-classes.Si nous voulons sauver les sous-classes de la surcharge des membres de la superclasse, nous pouvons les déclarer comme finales en utilisant le mot-clé final. Par exemple,
final int a=10;
final void display(){......}
rendre une méthode définitive garantit que la fonctionnalité définie dans la superclasse ne sera jamais modifiée de toute façon. De même, la valeur d'une variable finale ne peut jamais être modifiée. Les variables finales se comportent comme des variables de classe.la source
Strictement parlant des champs d' instance ,
final
pourrait légèrement améliorer les performances si un GC particulier veut exploiter cela. Lorsqu'un concurrentGC
se produit (cela signifie que votre application est toujours en cours d'exécution, alors que GC est en cours ), voyez ceci pour une explication plus large , les GC doivent utiliser certaines barrières lorsque les écritures et / ou les lectures sont effectuées. Le lien que je vous ai donné explique à peu près cela, mais pour être très bref: quand aGC
fait un travail simultané, tout lit et écrit dans le tas (pendant que le GC est en cours), est "intercepté" et appliqué plus tard dans le temps; afin que la phase concurrente du GC puisse terminer son travail.Par
final
exemple les champs, puisqu'ils ne peuvent pas être modifiés (sauf réflexion), ces barrières peuvent être omises. Et ce n'est pas qu'une pure théorie.Shenandoah GC
les a en pratique (mais pas pour longtemps ), et vous pouvez faire, par exemple:Et il y aura des optimisations dans l'algorithme GC qui le rendront légèrement plus rapide. C'est parce qu'il n'y aura pas de barrières interceptées
final
, puisque personne ne devrait jamais les modifier. Pas même par réflexion ou JNI.la source
La seule chose à laquelle je peux penser est que le compilateur pourrait optimiser les variables finales et les incorporer en tant que constantes dans le code, ainsi vous vous retrouverez sans mémoire allouée.
la source
absolument, tant que la durée de vie de l'objet est plus courte, ce qui donne un grand avantage à la gestion de la mémoire, nous avons récemment examiné la fonctionnalité d'exportation ayant des variables d'instance sur un test et un autre test ayant une variable locale au niveau de la méthode. pendant le test de charge, JVM lève outofmemoryerror lors du premier test et JVM s'est arrêté. mais dans le deuxième test, réussi à obtenir le rapport grâce à une meilleure gestion de la mémoire.
la source
Le seul moment où je préfère déclarer les variables locales comme finales est lorsque:
Je dois les rendre définitifs pour qu'ils puissent être partagés avec une classe anonyme (par exemple: créer un thread démon et lui permettre d'accéder à une valeur de la méthode englobante)
Je veux les rendre définitifs (par exemple: une valeur qui ne devrait pas / ne doit pas être remplacée par erreur)
la source