La gestion de la mémoire est un élément crucial du développement de logiciels informatiques, chargée de l'allocation, de l'utilisation et de la libération efficaces de la mémoire dans les applications. Son importance réside dans l'amélioration des performances du logiciel et la garantie de la stabilité du système.
Le garbage collection (GC) joue un rôle central dans les langages de programmation contemporains tels que Java et Go. Il détecte et recycle de manière autonome la mémoire inutilisée, évitant ainsi aux développeurs d'avoir à gérer manuellement la mémoire. Le concept de GC est apparu à l'origine dans le langage de programmation LISP à la fin des années 1950, marquant l'introduction de la gestion automatisée de la mémoire.
Les principaux avantages de la gestion automatisée de la mémoire sont les suivants :
Comprendre la nature des « déchets » dans la mémoire et identifier l'espace récupérable est essentiel. Dans les prochains chapitres, nous commencerons par explorer les principes fondamentaux du garbage collection.
L'algorithme de comptage de références attribue un champ dans l'en-tête de l'objet pour suivre son décompte de références. Ce nombre augmente à chaque nouvelle référence et diminue lorsqu'une référence est supprimée. Lorsque le décompte atteint zéro, l'objet est éligible pour le garbage collection.
Considérez le code suivant :
Créez d'abord une chaîne avec une valeur de démonstration référencée par d (Figure 1).
String d = new String("demo");
Figure 1 – Après la création d'une chaîne
Ensuite, définissez d sur null. Le nombre de références de la démo est nul. Dans l'algorithme de comptage de références, la mémoire pour la démonstration doit être récupérée (Figure 2).
d =null; // Reference count of 'demo' becomes zero, prompting garbage collection.
Figure 2 – Lorsque la référence est annulée
L'algorithme de comptage de références fonctionne pendant l'exécution du programme, évitant les événements Stop-The-World, qui arrêtent temporairement le programme pour le garbage collection. Cependant, son inconvénient majeur est l'incapacité à gérer les références circulaires (Figure 3).
Par exemple:
public class CircularReferenceDemo { public CircularReferenceDemo reference; private String name; public CircularReferenceDemo(String name) { this.name = name; } public void setReference(CircularReferenceDemo ref) { this.reference = ref; } public static void main(String[] args) { CircularReferenceDemo objA = new CircularReferenceDemo("Ref_A"); CircularReferenceDemo objB = new CircularReferenceDemo("Ref_B"); objA.setReference(objB); objB.setReference(objA); objA = null; objB = null; } }
Ici, malgré l'annulation des références externes, les références mutuelles entre objA et objB empêchent leur garbage collection.
Figure 3 – Références circulaires
Nous pouvons voir que les deux objets ne sont plus accessibles. Cependant, ils sont référencés les uns par les autres et leur nombre de références ne sera donc jamais nul. Par conséquent, le collecteur GC ne sera jamais averti de les récupérer à l’aide de l’algorithme de comptage de références.
Cet algorithme est pratiquement implémenté en C grâce à l'utilisation de std::shared_ptr. Conçu pour gérer le cycle de vie des objets alloués dynamiquement, std::shared_ptr automatise l'incrément et la décrémentation du nombre de références à mesure que les pointeurs vers l'objet sont créés ou détruits. Ce pointeur intelligent fait partie de la bibliothèque standard C, offrant des capacités robustes de gestion de la mémoire qui réduisent considérablement les risques associés à la gestion manuelle de la mémoire. Chaque fois qu'un std::shared_ptr est copié, le nombre de références internes de l'objet géré augmente, reflétant la nouvelle référence. À l'inverse, lorsqu'un std::shared_ptr est détruit, sort de la portée ou est réaffecté à un autre objet, le nombre de références diminue. La mémoire allouée est automatiquement récupérée et l'objet est détruit lorsque son compteur de référence atteint zéro,
empêchant efficacement les fuites de mémoire en garantissant qu'aucun objet ne reste alloué sans nécessité.
L'algorithme d'analyse d'accessibilité commence aux racines du GC, en parcourant le graphe d'objets. Les objets inaccessibles à partir de ces racines sont considérés comme irrécupérables et sont ciblés pour la collecte.
Comme le montre l'image ci-dessous, les objets dans le cercle bleu doivent être conservés en vie et les objets dans le cercle gris peuvent être recyclés (Figure 4).
Figure 4 – Fuite de mémoire
Cette méthode résout efficacement le problème des références circulaires inhérente à l'algorithme de comptage de références. Les objets inaccessibles depuis les racines du GC sont classés pour la collecte.
En règle générale, les objets Java considérés comme des racines GC incluent :
GraalVM propose un compilateur anticipé (AOT), qui traduit les applications Java en binaires exécutables autonomes appelés images natives GraalVM. Développés par Oracle Labs, ces binaires
encapsuler les classes d'applications et de bibliothèques, ainsi que les composants d'exécution tels que le GC, permettant des opérations sans environnement d'exécution Java (JRE).
Le processus implique une analyse statique pour déterminer les composants accessibles, une initialisation via des blocs exécutés et une finalisation en créant un instantané de l'état de l'application pour une traduction ultérieure du code machine.
La Substrate VM fait partie intégrante de la suite GraalVM, orchestrée par Oracle Labs. Il s'agit d'une JVM améliorée qui prend non seulement en charge la compilation anticipée (AOT), mais facilite également l'exécution de langages au-delà de Java, tels que JavaScript, Python, Ruby et même des langages natifs comme C et C. À la base, Substrate VM sert de cadre sophistiqué qui permet à GraalVM de compiler des applications Java dans des binaires natifs autonomes. Ces binaires ne s'appuient pas sur une machine virtuelle Java (JVM) conventionnelle pour leur exécution, ce qui rationalise le déploiement et
processus opérationnels.
L'une des caractéristiques cardinales de Substrate VM est son garbage collector spécialisé, optimisé pour les applications nécessitant une faible latence et une empreinte mémoire minimale. Ce garbage collector est apte à gérer la disposition de la mémoire et le modèle opérationnel uniques distincts des images natives, qui diffèrent considérablement des applications Java traditionnelles exécutées sur une JVM standard. L'absence d'un compilateur Just-In-Time (JIT) dans les images natives Substrate VM est un choix stratégique qui aide à minimiser la taille globale de l'exécutable. En effet, cela élimine la nécessité d'inclure le compilateur JIT et les métadonnées associées, qui sont importantes en taille et en complexité.
De plus, bien que GraalVM soit développé en Java, cela introduit certaines contraintes, notamment en termes d'accès à la mémoire native. Ces restrictions sont principalement dues à des problèmes de sécurité et à la nécessité de maintenir la compatibilité entre les différentes plates-formes. Cependant, l’accès à la mémoire native est essentiel pour des opérations optimales de garbage collection. Pour résoudre ce problème, Substrate VM utilise une suite d'interfaces spécialisées qui facilitent des interactions sûres et efficaces avec la mémoire native. Ces interfaces font partie de l'architecture GraalVM plus large et permettent à Substrate VM de gérer efficacement la mémoire d'une manière similaire aux langages de niveau inférieur comme C, tout en conservant la sécurité et la facilité de gestion de Java.
En pratique, ces capacités font de Substrate VM un outil extrêmement polyvalent qui améliore la fonctionnalité et l'efficacité des applications compilées avec GraalVM. En permettant aux développeurs de
exploite une gamme plus large de langages de programmation et les compile en binaires natifs efficaces, Substrate VM repousse les limites de ce qui peut être réalisé avec les environnements de développement Java traditionnels. Cela en fait un atout inestimable pour les projets de développement de logiciels modernes qui exigent des performances élevées, une consommation de ressources réduite et une prise en charge linguistique polyvalente.
Les éléments remarquables de Substrate VM incluent :
Accès simplifié à la mémoire via des interfaces telles que Pointer Interface Pointer pour les opérations de mémoire brute et WordBase Interface WordBase pour la gestion des valeurs de la taille d'un mot.
Division du tas en segments pré-initialisés contenant des objets immuables et des segments d'exécution pour l'allocation dynamique d'objets (Figure 5).
Figure 5 – Gestion de la mémoire dans une image native
Au moment de l'exécution, ce que l'on appelle le tas d'images dans Substrate VM contient des objets créés pendant le processus de création d'image. Cette section du tas est pré-initialisée avec les données de la section de données du binaire exécutable et est facilement accessible au démarrage de l'application. Les objets résidant dans le tas d'images sont considérés comme immortels ; par conséquent, les références au sein de ces objets sont traitées comme des pointeurs racine par le
éboueur. Cependant, le GC analyse uniquement certaines parties du tas d'images à la recherche de pointeurs racine, en particulier ceux qui ne sont pas marqués en lecture seule.
Pendant le processus de construction, les objets désignés comme en lecture seule sont placés dans une section spécifique en lecture seule du tas d'images. Étant donné que ces objets ne contiendront jamais de références aux objets alloués au moment de l'exécution, ils ne contiennent aucun pointeur racine, ce qui permet au GC de les contourner lors des analyses. De même, les objets constitués uniquement de données primitives ou de tableaux de types primitifs manquent également de pointeurs racine. Cet attribut rationalise davantage le processus de récupération de place, car ces objets peuvent être omis des analyses GC.
En revanche, le tas Java est conçu pour contenir des objets ordinaires créés dynamiquement pendant l'exécution. Cette partie du tas est soumise à un garbage collection régulier pour récupérer l'espace occupé par les objets qui ne sont plus utilisés. Il est structuré comme un tas générationnel avec des mécanismes de vieillissement, facilitant une gestion efficace de la mémoire au fil du temps.
Cette division entre le tas d'images immortelles pré-initialisées et le tas Java géré dynamiquement permet à Substrate VM d'optimiser l'utilisation de la mémoire et l'efficacité du garbage collection, en répondant à la fois aux aspects statiques et dynamiques des besoins en mémoire des applications.
Dans le modèle de tas de Substrate VM, la mémoire est systématiquement organisée en structures appelées morceaux de tas. Ces morceaux, généralement dimensionnés par défaut à 1 024 Ko, forment un segment continu de mémoire virtuelle uniquement alloué au stockage d'objets. La structure organisationnelle de ces morceaux est une liste chaînée où le morceau de queue représente le segment le plus récemment ajouté. Un tel modèle
facilite l'allocation efficace de la mémoire et la gestion des objets.
Ces morceaux de tas sont en outre classés en deux types : alignés et non alignés. Les morceaux de tas alignés sont capables de contenir plusieurs objets en continu. Cet alignement permet une cartographie plus simple de
objets vers leurs morceaux de tas parents respectifs, rendant la gestion de la mémoire plus intuitive et efficace. Dans les scénarios où la promotion d'objets est nécessaire, généralement lors du garbage collection et
optimisation de la mémoire : un objet est déplacé de son emplacement d'origine dans un morceau de tas parent vers un morceau de tas cible situé dans un « ancien espace » désigné. Cette migration fait partie de la stratégie de gestion générationnelle du tas qui aide à optimiser le processus de récupération de place en séparant les objets jeunes des anciens, réduisant ainsi la surcharge pendant les cycles GC.
GraalVM Native Image prend en charge divers GC adaptés à différents besoins :
Serial GC : Collecteur par défaut à faible encombrement adapté aux applications monothread.
G1 Garbage Collector : Conçu pour les applications multithread avec de grandes tailles de tas, améliorant la flexibilité dans la gestion de la génération.
Epsilon GC : Un collecteur minimaliste qui gère l'allocation mais manque de récupération, mieux utilisé pour les applications de courte durée où l'utilisation complète du tas est prévisible.
En conclusion, Substrate VM optimise efficacement la gestion de la mémoire au sein de GraalVM en incorporant des techniques avancées telles que le garbage collection spécialisé et la gestion structurée du tas. Ces fonctionnalités, notamment les fragments de tas et les segments de mémoire séparés pour les tas d'images et Java, rationalisent le garbage collection et améliorent les performances des applications. Comme Substrate VM prend en charge une variété de langages de programmation et les compile en binaires natifs efficaces, il montre comment les frameworks JVM modernes peuvent s'étendre au-delà des frontières traditionnelles pour améliorer l'efficacité d'exécution et la robustesse dans divers environnements d'application. Cette approche établit une norme élevée pour les développements futurs de la technologie des machines virtuelles et du déploiement d'applications.
Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.
Copyright© 2022 湘ICP备2022001581号-3