Schéma de structure interne JVM
Les machines virtuelles Java sont principalement divisées en cinq zones: zone de méthode, tas, pile Java, registre PC et pile de méthode locale. Jetons un coup d'œil à certains problèmes importants concernant la structure JVM.
1. Quelles zones sont partagées? Lesquels sont privés?
La pile Java, la pile de méthode locale et le compteur de programmes sont établies et détruites lorsque le thread utilisateur démarre et se termine.
Chaque fil a ces zones indépendantes. La zone de méthode et le tas sont partagés par tous les threads de tout le processus JVM.
2. Qu'est-ce qui est sauvé dans la zone de la méthode? Sera-t-il recyclé?
La zone de méthode n'est pas seulement des informations et du code de la méthode enregistrés. Dans le même temps, dans une sous-région appelée pool constant d'exécution, diverses références symboliques dans la table constante dans le fichier de classe, ainsi que des références directes traduites. Ces informations sont accessibles via un objet de classe dans le tas en tant qu'interface.
Bien que les informations de type soient stockées dans la zone de la méthode, elles seront également recyclées, mais les conditions de recyclage sont relativement strictes:
(1) Toutes les instances de cette classe ont été recyclées
(2) le chargeur de classe de ce cours a été recyclé
(3) L'objet de classe de cette classe n'est référencé nulle part (y compris la classe.
3. Le contenu du pool constant dans la zone de la méthode est-il inchangé?
Le pool constant d'exécution dans la zone de méthode enregistre les données dans le pool constant statique dans le fichier de classe. En plus de stocker diverses références littérales et symboliques générées au moment de la compilation, il contient également des références directes traduites. Mais cela ne signifie pas que le pool constant ne changera pas pendant l'exécution. Par exemple, lors de l'exécution, vous pouvez appeler la méthode des stagiaires de la chaîne et mettre de nouvelles constantes de chaîne dans le pool.
package com.cdai.jvm; classe publique RuntimeConstantPool {public static void main (String [] args) {String S1 = new String ("Hello"); String s2 = new String ("Hello"); System.out.println ("AVANT INTERNE, S1 == S2:" + (S1 == S2)); s1 = s1.intern (); s2 = s2.intern (); System.out.println ("After Intern, S1 == S2:" + (S1 == S2)); }}
4. Toutes les instances d'objet sont-elles allouées sur le tas?
Avec la maturité progressive de la technologie d'analyse d'échappement, les technologies d'allocation sur les places et de remplacement scalaire ont rendu "tous les objets alloués sur le tas" moins absolus.
La soi-disant évasion signifie que lorsque le pointeur d'un objet est référencé par plusieurs méthodes ou threads, nous disons que ce pointeur s'échappe.
D'une manière générale, les objets Java sont alloués dans le tas, et seuls les pointeurs de l'objet sont enregistrés dans la pile. En supposant qu'une variable locale ne s'échappe pas lors de l'exécution de la méthode (exposée à l'extérieur de la méthode), elle sera allouée directement sur la pile, puis continuera d'être exécutée dans la pile d'appels. Après l'exécution de la méthode, l'espace de pile est recyclé et les variables locales sont également recyclées. Cela réduit l'allocation d'un grand nombre d'objets temporaires dans le tas et améliore l'efficacité du recyclage GC.
De plus, l'évacuation de l'évacuation ometra également les verrous sur les variables locales qui ne se sont pas échappées et omettent les verrous détenus sur cette variable.
Lors de l'activation de la méthode d'analyse d'échappement, ajoutez des paramètres de démarrage JVM: -xx: + est-ce que la rééducation est?
5. Combien de façons y a-t-il pour accéder aux objets sur le tas?
(1) Accès direct au pointeur
La référence sur la pile enregistre un pointeur vers un objet sur le tas, et vous pouvez localiser l'objet en un seul coup, qui a une vitesse d'accès plus rapide.
Cependant, lorsque les objets sont déplacés dans le tas (chaque objet est souvent déplacé pendant la collecte des ordures), la valeur de la variable du pointeur sur la pile doit également être modifiée. Actuellement, JVM Hotspot adopte cette méthode.
(2) Accès indirect à la manche
La référence sur la pile pointe vers une poignée dans le pool de poignée, et l'objet est accessible via la valeur de cette poignée. Par conséquent, la poignée est comme un pointeur secondaire, qui nécessite deux positionnement pour accéder à l'objet, ce qui est plus lent que le positionnement du pointeur direct, mais lorsque l'objet se déplace dans le tas, il n'est pas nécessaire de modifier la valeur référencée sur la pile.
Comment déborder JVM
Après avoir compris le rôle des cinq domaines de mémoire des machines virtuelles Java, continuons à apprendre dans quelles circonstances ces domaines débordent.
1. Configuration des paramètres de la machine virtuelle
-XMS: La taille initiale du tas, la valeur par défaut est 1/64 de mémoire physique (<1 Go); Par défaut (paramètre Minheapfreeratio peut être ajusté) Lorsque la mémoire de tas libre est inférieure à 40%, le JVM augmentera le tas jusqu'à la limite maximale de -xmx.
-Xmx: taille maximale du tas, par défaut (paramètre maxheapfreeratio peut être ajusté) lorsque la mémoire de tas libre est supérieure à 70%, le JVM réduira le tas jusqu'à la limite minimale de -XMS.
-XSS: Taille de pile pour chaque thread. Après JDK5.0, la taille de la pile de chaque fil est de 1 m et dans le passé, la taille de la pile de chaque fil est de 256k. Il doit être ajusté de manière appropriée en fonction de la taille de mémoire requise du thread de l'application. Dans la même mémoire physique, la réduction de cette valeur peut générer plus de threads. Cependant, le système d'exploitation a toujours une limite sur le nombre de threads dans un processus et ne peut pas être généré à l'infini, avec des valeurs d'expérience allant d'environ 3000 à 5000. Généralement, de petites applications, si la pile n'est pas très profonde, 128k devrait être suffisant. Pour les grandes applications, 256k est recommandé. Cette option a un grand impact sur les performances et nécessite des tests stricts.
-Xx: permsize: définissez la valeur initiale de la génération permanente (gén perm). La valeur par défaut est 1/64 de mémoire physique.
-Xx: MaxPerMSize: définissez la valeur maximale de la génération persistante. 1/4 de mémoire physique.
2. Méthode Orient Overflow
Étant donné que la zone de méthode contient les informations pertinentes de la classe, lorsque nous chargeons trop de classes, la zone de méthode débordera. Ici, nous essayons de déborder de la zone de méthode à travers deux méthodes: JDK Dynamic Proxy et CGLIB Proxy.
2.1 Proxy dynamique JDK
Package com.cdai.jvm.Overflow; import java.lang.reflect.invocationhandler; import java.lang.reflect.method; import java.lang.reflect.proxy; classe publique MethodaReaoverflow {interface statique Oomiinterface {} classe statique OomObject implémente oomiInterface {} classe statique oomObject2 implémente oomiInterface {} public static void main (String [] args) {final oomobject object = new ooMobject (); while (true) {oomiInterface proxy = (oomiinterface) proxy.newproxyinstance (thread.currentThread (). getContextClassLoader (), oomObject.class.getInterfaces (), la méthode invocationhandler () {@OverRide Object Invoke (Object Proxy, méthode, objet, objet [] args) Throws Throws {TROMABLE { System.out.println ("Interceptor1 fonctionne"); System.out.println (proxy.getClass ()); System.out.println ("proxy1:" + proxy); Oomiinterface proxy2 = (oomiinterface) proxy.newproxyinstance (thread.currentThread (). GetContextClassLoader (), oomObject.class.getInterfaces (), new invocationhandler () {@Override Object Invoke (Object Proxy, méthode d'interception [] args) lance throws {System. fonctionne "); return method.invoke (objet, args);}}); System.out.println (proxy2.getClass ()); System.out.println ("proxy2:" + proxy2); }}} Bien que nous continuons à appeler la méthode proxy.newinstance () pour créer la classe Proxy, le JVM n'a pas de débordement de mémoire.
Chaque appel génère une instance différente de la classe proxy, mais l'objet de classe de la classe proxy n'a pas changé. Est-ce proxy
La classe a-t-elle un cache pour l'objet de classe de la classe proxy? Les raisons spécifiques seront analysées en détail dans le "JDK Dynamic Proxy ultérieur et CGLIB".
2.2 Agent CGLIB
CGLIB mettra également en cache l'objet de classe de la classe proxy, mais nous pouvons le configurer pour ne pas mettre en cache l'objet de classe.
De cette façon, le but de déborder la zone de méthode peut être atteint en créant à plusieurs reprises des classes de proxy.
Package com.cdai.jvm.Overflow; import java.lang.reflect.method; importer net.sf.cglib.proxy.enhancer; importer net.sf.cglib.proxy.MethodInterceptor; importer net.sf.cglib.proxy.methodproxy; classe publique MethodAreAoverflow2 {classe statique oomObject {} public static void main (string [] args) {while (true) {Enhancer Enhancer = new Enhancer (); Enhancer.SetSuperclass (oomObject.class); Enhancer.SetUseCache (false); Enhancer.SetCallback (new MethodInterceptor () {@Override Public Object Intercept (objet obj, méthode méthode, objet [] args, méthodyproxy proxy) lance throws {return metheth.invoke (obj, args);}}); OomObject proxy = (oomObject) Enhancer.Create (); System.out.println (proxy.getClass ()); }}}
3. Chasse déborde
Le débordement du tas est relativement simple. Vous pouvez déborder le tas en créant un grand objet de tableau.
Package com.cdai.jvm.Overflow; classe publique HeapoverFlow {private static final int mb = 1024 * 1024; @SuppressWarnings ("inutilisé") public static void main (String [] args) {byte [] bigMemory = new octet [1024 * MB]; }}
4. Stack Overflow
Le débordement de pile est également courant. Parfois, lorsque l'appel récursif que nous écrivons n'a pas la condition de terminaison correcte, la méthode continuera de se reproduire, la profondeur de la pile continuera d'augmenter, et finalement le débordement de la pile se produira.
Package com.cdai.jvm.Overflow; classe publique stackOverflow {private static int stackDepth = 1; public static void stackOverflow () {stackDepth ++; stackOverflow (); } public static void main (string [] args) {try {stackOverflow (); } catch (exception e) {System.err.println ("Stack Depth:" + stackDepth); e.printStackTrace (); }}}