Depuis le travail, de plus en plus de code a été écrit, le programme est devenu de plus en plus gonflé et l'efficacité est devenue de moins en moins. Ce n'est absolument pas autorisé à un programmeur comme moi qui poursuit la perfection. Par conséquent, en plus d'optimiser constamment la structure du programme, l'optimisation de la mémoire et le réglage des performances sont devenus mes "astuces" habituelles.
Pour optimiser et régler la mémoire et les performances des programmes Java, il n'est certainement pas possible de ne pas comprendre les principes internes des machines virtuelles (ou des spécifications plus rigoureuses). Voici un bon livre "Java Virtual Machine (deuxième édition)" (par Bill Venners, traduit par Cao Xiaogang et Jiang Jing. En fait, cet article est la compréhension personnelle de l'auteur des machines virtuelles Java après avoir lu ce livre). Bien sûr, les avantages de la compréhension des machines virtuelles Java ne se limitent pas aux deux avantages ci-dessus. D'un point de vue technique plus profond, la compréhension des spécifications et de la mise en œuvre des machines virtuelles Java sera plus utile pour que nous écrivons du code Java efficace et stable. Par exemple, si nous comprenons le modèle de mémoire de la machine virtuelle Java et le mécanisme de recyclage de la mémoire de la machine virtuelle, nous ne compterons pas trop dessus, mais nous "publierons explicitement la mémoire" en cas de besoin (le code Java ne peut pas libérer explicitement la mémoire, mais nous pouvons informer le collecteur des ordures que l'objet doit être recyclé en relâchant la référence de l'objet), afin de réduire la consommation de mémoire innombrable; Si nous comprenons comment fonctionne la pile Java, nous pouvons réduire le risque de débordement de pile en réduisant le nombre de couches récursives et le nombre de boucles. Pour les développeurs d'applications, ils peuvent ne pas impliquer directement le travail de la mise en œuvre sous-jacente de ces machines virtuelles Java, mais la compréhension de ces connaissances de base aura plus ou moins un impact subtil et bon sur les programmes que nous écrivons.
Cet article expliquera brièvement le modèle d'architecture et de mémoire de la machine virtuelle Java. S'il y a des mots inappropriés ou des explications inexactes, assurez-vous de les corriger. Je suis très honoré!
Architecture de la machine virtuelle Java
Sous-système de chargement de classe
Il y a deux chargeurs de classe pour les machines virtuelles Java, à savoir le chargeur de classe de démarrage et le chargeur défini par l'utilisateur.
La classe de chargement de chargement charge la classe dans la zone de données d'exécution via le nom entièrement qualifié de la classe (nom du package et nom de classe, le montage du réseau comprend également l'URL). Pour chaque type chargé, la machine virtuelle Java crée une instance de la classe java.lang.class pour représenter le type, qui est placé dans la zone du tas en mémoire, et les informations de type chargé se trouvent dans la zone de méthode, qui est la même que tous les autres objets.
Avant de charger un type, le sous-système de chargement de classe doit non seulement localiser et importer le fichier de classe binaire correspondant, mais également vérifier l'exactitude de la classe importée, allouer et initialiser la mémoire pour les variables de classe et les références de symbole d'analyse comme références directes. Ces actions sont strictement dans l'ordre suivant:
1) Chargement - Rechercher et charger les données binaires de type;
2) Connexion - Effectuer la vérification, la préparation et l'analyse (facultatif)
3) Vérifiez pour assurer l'exactitude du type importé
4) Préparez-vous à allouer la mémoire aux variables de classe et à les initialiser aux valeurs par défaut
5) Analyser la référence symbolique dans le type d'application directe
Zone de méthode
Pour chaque type chargé par le sous-système de chargement de classe, la machine virtuelle enregistre les données suivantes dans la zone de la méthode:
1. Nom de type pleinement qualifié
2. Nom entièrement qualifié de Type Superclass (java.lang.object n'a pas de superclasse)
3. Est le type de classe A ou un type d'interface
4. Modificateur d'accès de type
5. Nom entièrement qualifié Liste commandée de toute hyperinterface directe
En plus des informations de type de base ci-dessus, les informations suivantes seront également enregistrées:
6. Type de piscine constante
7. Informations sur le terrain (y compris le nom de champ, type de champ, modificateur de champ)
8
9. Toutes les variables de classe sauf les constantes (en fait, ce sont des variables statiques de la classe. Parce que les variables statiques sont partagées par tous les instances et sont directement liées au type, ce sont des variables au niveau de la classe et sont enregistrées dans le domaine de la méthode en tant que membres de la classe)
10. Une référence à Classloader
// le renvoyé est la classe de référence classloader.class.getClassloader () qui a été enregistrée tout à l'heure; une référence à la classe de classe // il renverra la chaîne de référence.classe de la classe de classe vient d'être enregistrée tout à l'heure;
Notez que la zone de méthode peut également être recyclée par le collecteur des ordures.
tas
Toutes les instances de classe ou les tableaux créés par les programmes Java lors de l'exécution sont placés dans le même tas, et chaque machine virtuelle Java a également un espace de tas, et tous les threads partagent un tas (c'est pourquoi un programme Java multithread provoquera des problèmes de synchronisation dans l'accès à l'objet).
Étant donné que chaque machine virtuelle Java a des implémentations différentes de la spécification de la machine virtuelle, nous ne savons peut-être pas quelle forme chaque machine virtuelle Java représente les instances d'objet dans le tas, mais nous pouvons avoir un aperçu des implémentations possibles suivantes:
Comptoir du programme
Pour exécuter les programmes Java, chaque fil a son propre registre PC (compteur de programme), qui est créé lorsque le thread démarre, avec une taille d'un mot, et est utilisé pour enregistrer l'emplacement de la ligne de code suivante qui doit être exécutée.
Pile java
Chaque fil a une pile Java, ce qui enregistre l'état en cours d'exécution du fil en unités de cadres de pile. Il existe deux types d'opérations de machines virtuelles sur la pile Java: la suppression et l'empilement de pile, tous deux ont des cadres. Le cadre de pile enregistre des données telles que les paramètres entrants, les variables locales, les résultats de fonctionnement intermédiaire, etc., qui sont apparus lorsque la méthode est terminée puis libérée.
Jetez un œil à l'instantané de mémoire du cadre de pile lorsque deux variables locales sont ajoutées ensemble
Pile de méthode locale
C'est là que Java appelle la bibliothèque locale du système d'exploitation, utilisée pour implémenter JNI (Java Native Interface, Java Interface local)
Moteur d'exécution
Le noyau de la machine virtuelle Java contrôle le chargement de bytecode Java et l'analyse; Pour exécuter des programmes Java, chaque thread est une instance d'un moteur d'exécution de machine virtuelle indépendante. Du début à la fin du cycle de vie du fil, il exécute Bytecode ou exécute des méthodes locales.
Interface locale
Connecté à la pile de méthode locale et à la bibliothèque du système d'exploitation.
Remarque: Tous les lieux mentionnés dans l'article se réfèrent à "Java Virtual Machine Spécifications pour les plates-formes Javaee et Javase".
Pratique d'optimisation de la mémoire virtuelle de la machine
Étant donné que la mémoire est mentionnée, les fuites de mémoire doivent être mentionnées. Comme nous le savons tous, Java s'est développé à partir de la base de C ++, et un gros problème avec les programmes C ++ est que les fuites de mémoire sont difficiles à résoudre. Bien que Java de JVA ait son propre mécanisme de collecte de déchets pour recycler la mémoire, dans de nombreux cas, les développeurs de programmes Java n'ont pas trop de problème, mais il y a aussi des problèmes de fuite, qui sont un peu plus petits que C ++. Par exemple, il existe un objet référencé mais inutile dans le programme: si le programme fait référence à l'objet, mais ne peut pas l'utiliser à l'avenir, alors l'espace mémoire qu'il prend est gaspillé.
Voyons d'abord comment fonctionne GC: surveiller l'état de fonctionnement de chaque objet, y compris l'application, la citation, la citation, la mission, etc. De nombreux programmeurs Java comptent trop sur GC, mais la clé du problème est que peu importe la qualité du mécanisme de collecte des ordures de JVM, la mémoire est toujours une ressource limitée. Par conséquent, même si GC terminera la majeure partie de la collecte des ordures pour nous, il est toujours nécessaire de prêter attention à l'optimisation de la mémoire pendant le processus de codage de manière appropriée. Cela peut réduire efficacement le nombre de GC, tout en améliorant l'utilisation de la mémoire et en maximisant l'efficacité du programme.
Dans l'ensemble, l'optimisation de la mémoire des machines virtuelles Java devrait commencer à partir de deux aspects: les machines virtuelles Java et les applications Java. Le premier fait référence au contrôle de la taille de la partition de mémoire logique de la machine virtuelle via des paramètres de machine virtuelle en fonction de la conception de l'application afin que la mémoire de la machine virtuelle complète les exigences de mémoire du programme; Ce dernier fait référence à l'optimisation des algorithmes du programme, à la réduction de la charge GC et à l'amélioration du taux de réussite du recyclage GC.
Les paramètres d'optimisation de la mémoire de la machine virtuelle via les paramètres sont les suivants:
Xms
Taille du tas initial
Xmx
valeur maximale de tas java
1mn
Taille du tas de la jeune génération
XSS
Taille de pile pour chaque fil
Ce qui précède est trois paramètres plus couramment utilisés, certains:
Xx: Minheapfreeratio = 40
Pourcentage minimum de tas libre après GC pour éviter l'expansion.
Xx: maxheapfreeratio = 70
Pourcentage maximum de tas sans GC pour éviter le rétrécissement.
Xx: newratio = 2
Ratio des tailles nouvelles / anciennes. [SPARC -Client: 8; x86 -server: 8; x86 -Client: 12.] - Client: 8 (1.3.1+), x86: 12]
Xx: NewSize = 2,125m
La taille par défaut de la nouvelle génération (en octets) [5.0 et plus récente: les machines virtuelles 64 bits sont à l'échelle de 30% plus grande; x86: 1m; x86, 5,0 et plus: 640k]
Xx: maxnewsize =
Taille maximale de la nouvelle génération (en octets). Depuis 1.4, MaxNewSize est calculé en fonction de Newratio.
Xx: Survivorratio = 25
Rapport de la taille de l'espace Eden / Survivor [SPARC en 1.3.1: 25; Autres plates-formes Solaris en 5.0 et plus tôt: 32]
Xx: permsize =
Taille initiale de la génération permanente
Xx: maxpermSize = 64m
Taille de la génération permanente. [5.0 et plus récents: les machines virtuelles 64 bits sont mises à l'échelle de 30% plus grandes; 1,4 AMD64: 96M; 1.3.1 -Client: 32m.]
Ce qui est mentionné ci-dessous pour améliorer l'utilisation de la mémoire et réduire les risques de mémoire en optimisant les algorithmes de programme est entièrement empirique et est à titre de référence uniquement. S'il y a une inappropriation, veuillez me corriger, merci!
1. Libérez la référence des objets inutiles dès que possible (xx = null;)
Regardez un morceau de code:
public list <pagedata> parse (htmlpage page) {list <pagedata> list = null; try {list valuellist = page.getByxPath (config.getContentXPath ()); if (ValuElist == NULL || VALUELIST.ISEMPTY ()) {RETOUR LISTE; } // Créez un objet en cas de besoin, enregistrez la mémoire et améliorez la liste d'efficacité = nouveau ArrayList <Pagedata> (); Pagedata Pagedata = new Pagedata (); StringBuilder Value = new StringBuilder (); for (int i = 0; i <valuellist.size (); i ++) {htmlelement contenu = (htmlelement) valuellist.get (i); DomNodeList <Htmlelement> imgs = tent.getElementsByTagName ("img"); if (imgs! = null &&! imgs.isempty ()) {for (htmlelement img: imgs) {try {htmlimage image = (htmlimage) img; String path = image.getsrcattribute (); String format = path.substring (path.lastIndexof ("."), Path.length ()); String localPath = "d: / images /" + md5helper.md5 (path) .replace ("//", ","). Remplacer ("/", ",") + format; Fichier localfile = nouveau fichier (localPath); if (! localfile.exists ()) {localFile.CreateEwFile (); Image.Saveas (localfile); } image.setAttribute ("src", "fichier: ////" + localPath); localfile = null; image = null; img = null; } catch (exception e) {}} // Cet objet ne sera pas utilisé à l'avenir. Effacer la référence à celui-ci équivaut à dire à l'avance GC. L'objet peut recycler IMGS = null; } String text = content.asxml (); value.append (text) .append ("<br/>"); VAUELIST = NULL; contenu = null; text = null; } pagedata.setContent (value.toString ()); Pagedata.SetchArset (page.getPageEncoding ()); list.add (pagedata); // le pagedata = null; est inutile car la liste contient toujours la référence à l'objet, et GC ne le recyclera pas valeur = null; // il n'y a pas de liste = null ici; Parce que la liste est la valeur de retour de la méthode, sinon la valeur de retour que vous obtenez de la méthode sera toujours vide, et ce type d'erreur n'est pas facile à découvrir ou à exclure} catch (exception e) {} return list; }2. Utilisez soigneusement les types de données de collecte, tels que les tableaux, les arbres, les graphiques, les listes liées et autres structures de données. Ces structures de données sont plus compliquées à recycler pour GC.
3. Évitez de postuler explicitement pour un espace de tableau. Lorsque vous devez appliquer explicitement, essayez d'estimer sa valeur raisonnable aussi précisément que possible.
4. Essayez d'éviter de créer et d'initialiser un grand nombre d'objets dans le constructeur par défaut de la classe et d'empêcher le gaspillage de ressources de mémoire inutile lors de l'appel de son propre constructeur de la classe.
5. Essayez d'éviter le système forcé à recycler la mémoire des ordures et augmenter la dernière heure du recyclage des ordures dans le système
6. Essayez d'utiliser des variables de valeur instantanée lors du développement d'applications d'appel de méthode distante, sauf si l'appelant distant doit obtenir la valeur de la variable de valeur instantanée.
7. Essayez d'utiliser la technologie de mise en commun d'objets dans des scénarios appropriés pour améliorer les performances du système