Un excellent programmeur Java doit comprendre comment fonctionne GC, comment optimiser les performances de GC et comment avoir une interaction limitée avec GC, car certaines applications ont des exigences de performance, telles que les systèmes embarqués, les systèmes en temps réel, etc., qui ne peuvent être que amélioré de manière globale. Cet article présente d'abord brièvement le principe de travail de GC, puis mène des discussions approfondies sur plusieurs questions clés de GC, et met enfin quelques suggestions de programmation Java pour améliorer les performances des programmes Java du point de vue du GC.
Les principes de base de GC
La gestion de la mémoire de Java est en fait la gestion des objets, y compris l'allocation et la libération d'objets.
Pour les programmeurs, le nouveau mot-clé est utilisé pour allouer des objets; .Gc sera responsable de la récupération de l'espace mémoire de tous les objets "inaccessibles /".
Pour GC, lorsqu'un programmeur crée un objet, GC commence à surveiller l'adresse, la taille et l'utilisation de l'objet. Généralement, GC utilise des graphiques dirigés pour enregistrer et gérer tous les objets du tas (tas) (voir référence 1 pour plus de détails). De cette façon, quels objets sont / «accessibles /» et quels objets sont / «inaccessibles /». Cependant, afin de s'assurer que GC peut être mis en œuvre sur différentes plateformes, les spécifications Java ne stipulent pas strictement de nombreux comportements de GC. Par exemple, il n'y a pas de réglementation claire sur des questions importantes telles que le type d'algorithme de recyclage à utiliser et quand effectuer le recyclage. Par conséquent, différents implémentateurs JVM ont souvent différents algorithmes d'implémentation. Cela apporte également l'incertitude au développement des programmeurs Java. Cet article examine plusieurs questions liées au travail de GC et s'efforce de réduire l'impact négatif de cette incertitude sur les programmes Java.
GC incrémentiel (GC incrémentiel)
GC est généralement mis en œuvre dans JVM par un ou un groupe de processus. Par conséquent, lorsque le GC fonctionne longtemps, l'utilisateur peut ressentir la pause du programme Java. De nombreux objets qui devraient être recyclés qui n'ont pas été recyclés. Par conséquent, lors de la conception de GC, il est nécessaire de peser le temps de pause et le taux de récupération. Une bonne implémentation GC permet aux utilisateurs de définir les paramètres dont ils ont besoin. D'autres jeux en ligne en temps réel ne peuvent pas permettre des interruptions à long terme du programme. Le GC incrémentiel doit diviser une interruption à long terme en de nombreuses petites interruptions via un certain algorithme de recyclage, réduisant ainsi l'impact de GC sur les programmes utilisateur. Bien que le GC incrémentiel puisse ne pas être aussi efficace qu'un GC ordinaire dans les performances globales, il peut réduire le temps de pause maximum du programme.
Le hotspot JVM fourni par Sun JDK peut prendre en charge GC incrémentiel. La mise en œuvre de Hotspot JVM incrémentiel GC consiste à utiliser l'algorithme GC Train. Son idée de base est de regrouper tous les objets du tas (hiérarchiquement) en fonction de la création et de l'utilisation, de mettre des objets avec une utilisation fréquente et très pertinente dans une file d'attente et de continuer à se regrouper au fur et à mesure que le programme exécute l'ajustement. Lorsque GC s'exécute, il recycle toujours les objets les plus anciens (rarement visités récemment) en premier, et si le groupe entier est des objets recyclables, GC recycle tout le groupe. De cette façon, chaque exécution GC ne recyclera qu'une certaine proportion d'objets inaccessibles pour assurer le fonctionnement fluide du programme.
Explication détaillée de la fonction finaliser
Finalise est une méthode située dans la classe d'objets. Étant donné que la fonction finalisée n'implémente pas automatiquement les appels de chaîne, nous devons les implémenter manuellement, de sorte que la dernière instruction de la fonction finalisée est généralement super.finalize (). De cette façon, nous pouvons implémenter l'appel pour implémenter Finalize de bas en haut, c'est-à-dire d'abord libérer nos propres ressources, puis publier les ressources de la classe parent.
Selon la spécification du langage Java, le JVM garantit que cet objet est inaccessible avant d'appeler la fonction finalisée, mais le JVM ne garantit pas que cette fonction sera appelée. De plus, la spécification garantit également que la fonction finalisée s'exécute au plus une fois.
De nombreux débutants Java penseront que cette méthode est similaire au destructeur en C ++ et met la libération de nombreux objets et ressources dans cette fonction. En fait, ce n'est pas un bon moyen. Il y a trois raisons. Deuxièmement, une fois l'exécution finalisée terminée, l'objet peut devenir accessible et GC doit vérifier à nouveau si l'objet est accessible. Par conséquent, l'utilisation de finalisation réduira les performances de GC. Troisièmement, comme le moment où les appels GC Finalize sont incertains, il n'est également pas certain de libérer les ressources de cette manière.
Généralement, la finalisation est utilisée pour publier certaines ressources qui ne sont pas faciles à contrôler et sont très importantes, telles que certaines opérations d'E / S et les connexions de données. La publication de ces ressources est très critique pour l'ensemble de l'application. Dans ce cas, les programmeurs doivent gérer principalement ces ressources via le programme lui-même (y compris la libération) et compléter la méthode de libération des ressources avec la fonction finalisée comme un supplément pour former un mécanisme de gestion de l'assurance double, plutôt que de s'appuyer uniquement sur la finalisation pour libérer les ressources.
Voici un exemple pour illustrer qu'après que la fonction finalisée est appelée, elle peut toujours être accessible.
classe MyObject {Test Main; / Restaurer cet objet afin que cet objet puisse atteindre System.out.println (/ "Ceci est finalisé /"); // Utilisé pour tester Finalize pour exécuter une fois}} Class Test {MyObject Ref; ] args) {test test = nouveau test (); .ref! = null) System.out.println (/ "mon objet est toujours en vie /");
Résultats en cours:
C'est finaliser
MyObject est toujours vivant
Dans cet exemple, il convient de noter que bien que l'objet MyObject devienne un objet accessible dans Finalize, la prochaine fois que vous recyclez, la finalisation ne sera plus appelée car la fonction finalisée n'est appelée qu'une seule fois.
Comment le programme interagit avec GC
Java2 améliore les fonctions de gestion de la mémoire et ajoute un package java.lang.ref, qui définit trois classes de référence. Ces trois classes de référence sont des références, des références faibles et des fantômes. Les forces de référence de ces classes de référence sont entre objets accessibles et inaccessibles.
Il est également très facile de créer un objet de référence. Définissez la référence normale à Null. En même temps, nous appelons cet objet un objet de référence doux.
La principale caractéristique de la référence douce est qu'elle a de fortes fonctions de citation. Ce type de mémoire est recyclé uniquement lorsqu'il n'y a pas de mémoire insuffisante, donc en cas de mémoire suffisante, ils ne sont généralement pas recyclés. En outre, ces objets de référence peuvent également être définis sur Null avant que Java ne lance une exception d'OutofMemory. Voici le pseudocode d'utilisation suivant pour ce type de référence;
// Application Une image d'image image = new image (); // Créer un objet d'image ... // Utilisez l'image ... // Après avoir utilisé l'image, le définissez sur le type de référence doux et libérez une référence forte; = Nouveau SoftReference (Image); Image = Null; il doit être rechargé;
La plus grande différence entre les objets de référence faibles et les objets de référence doux est que lorsque GC recycle, il doit utiliser un algorithme pour vérifier si les objets de référence doux sont recyclés, tandis que GC recycle toujours les objets de référence faibles. Les objets de référence faibles sont plus faciles et plus rapides à recycler par GC. Bien que GC doit recycler les objets faibles lors de l'exécution, un groupe d'objets faibles avec des relations complexes nécessite souvent que plusieurs exécutions GC se terminent. Les objets de référence faibles sont souvent utilisés dans les structures de cartes pour désigner des objets avec de grands volumes de données.
Les références fantômes sont moins utiles et sont principalement utilisées pour aider à l'utilisation des fonctions finalisées. Les objets fantômes se réfèrent à certains objets qui ont rempli la fonction finalisée et sont des objets inaccessibles, mais ils n'ont pas été recyclés par GC. Ce type d'objet peut aider à finaliser dans certains travaux de recyclage ultérieurs.
Quelques suggestions de codage Java
Selon le principe de travail de GC, nous pouvons utiliser certaines compétences et méthodes pour que GC fonctionne plus efficacement et plus conforme aux exigences de l'application. Voici quelques suggestions de programmation.
1. La suggestion la plus élémentaire est de libérer les références d'objets inutiles dès que possible. Lorsque la plupart des programmeurs utilisent des variables temporaires, ils définiront automatiquement la variable de référence sur Null après avoir quitté le domaine actif (portée). Les graphiques, etc., ont une relation complexe entre ces objets. Pour ces objets, GC les recycle généralement moins efficaces. Si le programme le permet, affectez l'objet de référence inutilisé à Null dès que possible. [Page]
2. Essayez d'utiliser la fonction finalisée le moins possible. La fonction finalisée est une opportunité pour Java de fournir aux programmeurs l'occasion de publier des objets ou des ressources. Cependant, cela augmentera la charge de travail de GC, alors essayez d'utiliser Finalize le moins possible pour recycler les ressources.
3. Si vous avez besoin d'utiliser des images fréquemment utilisées, vous pouvez utiliser le type d'application Soft. Il peut enregistrer l'image en mémoire autant que possible pour que le programme appelle sans provoquer un outofMemory.
4. Faites attention aux types de données de collecte, y compris les structures de données telles que les tableaux, les arbres, les graphiques et les listes liées. Faites également attention à certaines variables globales, ainsi qu'à certaines variables statiques. Ces variables ont tendance à provoquer facilement des références pendantes, provoquant des déchets de mémoire.
5. Lorsque le programme a un certain temps d'attente, le programmeur peut exécuter manuellement System.gc () pour informer GC pour s'exécuter, mais la spécification du langage Java ne garantit pas que GC s'exécutera. L'utilisation de GC incrémentiel peut raccourcir le temps de pause des programmes Java.