L'utilisation de final pour les variables en Java améliore-t-elle la récupération de place?

StackOverflow https://stackoverflow.com/questions/306862

  •  08-07-2019
  •  | 
  •  

Question

Aujourd'hui, mes collègues et moi discutons de l'utilisation du mot clé final en Java pour améliorer le garbage collection.

Par exemple, si vous écrivez une méthode telle que:

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 la corbeille à papier à nettoyer la mémoire des variables inutilisées dans la méthode après la fermeture de la méthode.

Est-ce vrai?

Était-ce utile?

La solution

Voici un exemple légèrement différent, avec des champs de type référence ultimes 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. Le CPG devra suivre ce lien pour rechercher des objets vivants.

La machine virtuelle Java utilise un algorithme de GC par balayage, qui doit examiner toutes les références réelles dans la GC "Racine". emplacements (comme tous les objets de la pile d'appels en cours). Chaque objet en direct est marqué "&"; comme étant en vie, et tout objet désigné par un objet en direct est également marqué comme étant en vie.

Une fois la phase de marquage terminée, le CPG 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).

Par ailleurs, il est important de reconnaître que la mémoire heap 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 pépinière"). Comme la plupart des objets ont une courte durée de vie, 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 transféré dans l'ancienne génération (parfois appelée "génération permanente"), qui est traitée moins fréquemment.

Donc, par coeur, 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 consiste à éliminer le plus rapidement possible les références parasites. Vous pouvez le faire en affectant " null " à une référence d'objet dès que vous avez fini de l'utiliser.

Ou, mieux encore, réduisez la taille de la portée de chaque 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), l'objet peut rester en vie beaucoup plus longtemps. nécessaire.

Si vous utilisez de petites méthodes, avec seulement une douzaine de lignes de code, les objets déclarés dans cette méthode tomberont plus rapidement hors de portée et le GC sera en mesure de faire la plupart de ses travaux dans le contexte beaucoup plus simple. jeune génération plus efficace. Vous ne voulez pas que les objets soient déplacés dans l'ancienne génération sauf en cas d'absolue nécessité.

Autres conseils

La déclaration d'une variable locale final n'affectera pas la récupération de place, cela signifie simplement que vous ne pouvez pas modifier la variable. Votre exemple ci-dessus ne doit pas être compilé lorsque vous modifiez la variable totalWeight qui a été marquée final . En revanche, déclarer une primitive ( double au lieu de Double ) final permettra à cette variable d'être insérée dans le code appelant, de sorte que pourrait entraîner une amélioration de la mémoire et des performances. Ceci est utilisé lorsque vous avez plusieurs chaînes publiques statiques dans une classe.

En général, le compilateur et le moteur d’exécution optimisent leurs performances. Il est préférable d'écrire le code correctement et de ne pas essayer d'être trop compliqué. Utilisez final lorsque vous ne souhaitez pas que la variable soit modifiée. Supposez que le compilateur effectuera des optimisations simples. Si vous êtes préoccupé par les performances ou l'utilisation de la mémoire, utilisez un profileur pour déterminer le véritable problème.

Non, ce n'est absolument pas vrai.

N'oubliez pas que final ne signifie pas constante, cela signifie simplement que vous ne pouvez pas changer 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 le fait que la JVM n'aura jamais à modifier la référence (par exemple, ne pas avoir de vérification pour voir si elle a changé), mais elle serait si minime que vous ne devriez pas vous en soucier.

Final doit être considéré comme une métadonnée utile pour le développeur et non comme une optimisation du compilateur.

Quelques points à éclaircir:

  • La suppression de la référence ne devrait pas aider le GC. Si tel était le cas, cela indiquerait que vos variables sont trop étendues. Une exception est le cas du népotisme d'objet.

  • Il n'y a pas encore d'allocation sur pile pour Java.

  • La déclaration d'une variable finale signifie que vous ne pouvez pas (dans des conditions normales) affecter une nouvelle valeur à cette variable. Comme final ne dit rien à propos de la portée, il ne dit rien de son effet sur le GC.

Eh bien, je ne suis pas au courant de l'utilisation de l'option "finale". modificateur dans ce cas, ou 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 les primitives en boîte lorsque cela est requis par une API existante ou lorsque j'ai besoin de primitives nullables.

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 de la récupération de place en tant que telle. La seule chose à faire est que ces variables ne peuvent plus être annulées si elles ne sont plus utilisées (ce qui peut aider la récupération de place dans des situations de mémoire insuffisante).

Vous devez savoir que final permet au compilateur de formuler des hypothèses sur ce qu’il faut optimiser. Code en ligne et non compris le code connu pour être inaccessible.

final boolean debug = false;

......

if (debug) {
  System.out.println("DEBUG INFO!");
}

Le println ne sera pas inclus dans le code d'octet.

GC agit sur les références inaccessibles. Cela n'a rien à voir avec "final", qui est simplement une affirmation d'affectation unique. Est-il possible que le GC de certaines machines virtuelles utilise "&"; final "? Je ne vois ni comment ni pourquoi.

Il existe un cas peu connu parmi les éboueurs de génération en génération. (Pour une brève description, lisez la réponse à la benjismith pour un aperçu plus approfondi, lisez les articles à la fin).

Dans les GC de génération en génération, l’idée est que, la plupart du temps, seules les jeunes générations doivent être prises en compte. Les références sont analysées à l’emplacement de la racine, puis les objets de la nouvelle génération sont analysés. Lors de ces balayages plus fréquents, aucun objet de l'ancienne génération n'est vérifié.

Le problème vient du fait qu’un objet n’est pas autorisé à faire référence à 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 ramasse-miettes (voir l'article d'IBM sur collecteur de machine virtuelle Java hotspot ), affectant réellement les performances du GC.

La raison pour laquelle un ancien objet ne peut pas faire référence à un 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 désalloué à tort pendant la phase de balayage.

Bien sûr, comme l'ont souligné de nombreuses personnes, le mot-clé final n'affecte pas vraiment le récupérateur de mémoire, mais il garantit que la référence ne sera jamais changée en un objet plus jeune si cet objet survit aux collections mineures et passe à l'ancien. tas.

Articles:

IBM sur la récupération de place: historique , dans le < a href = "http://www.ibm.com/developerworks/java/library/j-jtp11253/" rel = "nofollow noreferrer"> JVM hotspot et performances . Celles-ci ne sont peut-être plus valables dans la mesure où elles remontent à 2003/04, mais elles permettent de mieux comprendre les GC.

Sun sur la Optimisation de la récupération de place

final sur les variables et les paramètres locaux n'a aucune incidence sur les fichiers de classe générés et ne peut donc pas affecter les performances d'exécution. Si une classe n'a pas de sous-classes, HotSpot la traite comme si elle était finale (elle peut être annulée ultérieurement si une classe qui casse cette hypothèse est chargée). Je crois que final sur les méthodes est à peu près la même chose 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 faite par javac sur cette base. final sur les champs donne à la machine virtuelle Java une certaine liberté d'ignorer les relations qui se passent avant les .

Il semble y avoir beaucoup de réponses qui sont des conjectures errantes. En réalité, il n’existe pas de modificateur final pour les variables locales au niveau du code intermédiaire. La machine virtuelle ne saura jamais que vos variables locales ont été définies comme définitives ou non.

La réponse à votre question est un non catégorique.

Toutes les méthodes et variables peuvent être remplacées par défaut dans les sous-classes. Si nous voulons enregistrer les sous-classes de la substitution des membres de la superclasse, nous pouvons les déclarer comme final à l'aide du mot-clé final. Par exemple        final int a = 10;        final void display () {......} Rendre une méthode finale garantit que les fonctionnalités définies dans la super-classe ne seront jamais modifiées 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 seule chose à laquelle je peux penser, c'est que le compilateur pourrait optimiser les variables finales et les insérer en tant que constantes dans le code, de sorte que vous n'aurez plus de mémoire allouée.

Absolument, tant que la vie de l’objet est plus courte et que la gestion de la mémoire présente de grands avantages, nous avons récemment examiné la fonctionnalité d’exportation comportant des variables d’instance sur un test et un autre portant une variable locale au niveau de la méthode. Au cours des tests de charge, la machine virtuelle Java (JVM) supprime l'erreur de mémoire principale lors du premier test et la machine virtuelle Java est arrêtée. mais lors du second test, nous avons réussi à obtenir le rapport grâce à une meilleure gestion de la mémoire.

La seule fois où je préfère déclarer les variables locales comme étant définitives, c'est lorsque:

  • Je dois les rendre finales pour pouvoir les partager avec une classe anonyme (par exemple: créer un thread démon et le laisser accéder à une valeur de la méthode englobante)

  • Je souhaite les rendre définitives (par exemple: une valeur qui ne doit pas / ne doit pas être annulée par erreur)

  
    
      

Contribuent-ils au ramassage rapide des ordures?
      Si je comprends bien, un objet devient un candidat de la collection GC s'il ne contient aucune référence forte à ce dernier. Dans ce cas également, rien ne garantit qu’ils seront immédiatement récupérés. En général, une référence forte est dite morte lorsqu'elle disparaît de la portée ou si l'utilisateur la réaffecte explicitement à une référence nulle. Ainsi, la déclaration finale signifie que la référence continuera d'exister jusqu'à ce que la méthode existe (à moins que sa portée ne soit explicitement réduite à un bloc interne spécifique {}) car vous ne pouvez pas réaffecter les variables finales. Je pense donc que la récupération de place Garbage 'finale' peut introduire un retard possible non souhaité.

    
  
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top