Contrairement au langage C, l'allocation et le recyclage de la mémoire Java (mémoire tas) sont automatiquement complétés par le garbage collector JVM. Cette fonctionnalité est très populaire et peut aider les programmeurs à mieux écrire du code. prend la machine virtuelle HotSpot comme exemple pour parler de Java GC.
Dans l'article sur la mémoire JVM, nous savons déjà que le tas Java est une zone mémoire partagée par tous les threads, y compris toutes les instances d'objets et les tableaux Toute allocation de mémoire est effectuée sur le tas. Afin d'effectuer un garbage collection efficace, la machine virtuelle divise la mémoire tas en trois zones : jeune génération, ancienne génération et génération permanente.
La nouvelle génération se compose d'Eden et de Survivor Space (S0, S1), et la taille est spécifiée par le paramètre -Xmn. Le rapport de taille de mémoire entre Eden et Survivor Space est par défaut de 8:1, qui peut être spécifié via le paramètre -XX:SurvivorRatio. Par exemple, lorsque la nouvelle génération est de 10 Mo, Eden se voit attribuer 8 Mo, et S0 et. S1 se voit attribuer chacun 1M.
Eden : grec, signifiant le jardin d'Eden. Dans la Bible, le jardin d'Eden signifie paradis. Selon les récits de la Genèse de l'Ancien Testament, Dieu a créé le premier monde dans le sien. propre image. Un homme, Adam, créa une femme, Ève, à partir d'une de ses côtes et la plaça dans le jardin d'Eden.
Dans la plupart des cas, les objets sont alloués dans Eden. Lorsqu'Eden ne dispose pas de suffisamment d'espace, un GC mineur sera déclenché. La machine virtuelle fournit le paramètre -XX : PrintGCDetails pour indiquer à la machine virtuelle d'imprimer la mémoire. lorsque la collecte des déchets a lieu.
Survivant : Cela signifie survivant et constitue la zone tampon entre la nouvelle génération et l'ancienne génération.
Lorsque le GC (Minor GC) se produit dans la nouvelle génération, les objets survivants seront déplacés vers la zone de mémoire S0 et la zone Eden sera effacée. Lorsque le Minor GC se reproduira, les objets survivants dans Eden et. S0 sera déplacé vers la zone mémoire S1.
Les objets survivants se déplaceront à plusieurs reprises entre S0 et S1 Lorsque l'objet passe d'Eden à Survivant ou entre Survivants, l'âge GC de l'objet s'accumule automatiquement lorsque l'âge GC dépasse le seuil par défaut de 15. Pour déplacer l'objet vers l'ancienne génération, vous pouvez définir le seuil d'âge du GC via le paramètre -XX:MaxTenuringThreshold.
La taille de l'espace de l'ancienne génération est la différence entre les deux paramètres -Xmx et -Xmn, qui sert à stocker les objets encore vivants après plusieurs GC mineurs. Lorsqu'il n'y a pas suffisamment d'espace dans l'ancienne génération, Major GC/Full GC sera déclenché, ce qui est généralement plus de 10 fois plus lent que Minor GC.
Dans l'implémentation HotSpot avant JDK8, les métadonnées de classe telles que les données de méthode, les informations de méthode (bytecode, pile et taille de variable), le runtime Le pool constant, les références de symboles déterminées et les tables de méthodes virtuelles sont enregistrées dans la génération permanente. La taille par défaut de la génération permanente est de 64 Mo pour 32 bits et de 85 Mo pour 64 bits. Elle peut être définie via le paramètre -XX:MaxPermSize. Si les métadonnées dépassent la taille de génération permanente, une exception MOO sera levée.
Dans HotSpot du JDK8, l'équipe de la machine virtuelle a supprimé la génération permanente du tas Java et a enregistré les métadonnées de la classe directement dans la zone de mémoire locale (mémoire hors tas), appelée métaespace.
Quels sont les avantages de faire cela ?
Les étudiants expérimentés trouveront que le processus de réglage de la génération permanente est très difficile à déterminer car il implique trop de facteurs, tels que le nombre total de classes, la taille de. le pool constant et le nombre de méthodes, et les données de la génération permanente peuvent se déplacer avec chaque GC complet.
Dans JDK8, les métadonnées de la classe sont stockées dans la mémoire locale. L'espace maximum allouable du métaespace est l'espace mémoire disponible du système, ce qui peut éviter le problème de débordement de mémoire de la génération permanente, mais la consommation de mémoire. doit être surveillé, une fois qu'une fuite de mémoire se produit, elle occupera une grande quantité de mémoire locale.
ps : Dans HotSpot avant JDK7, les chaînes du pool de constantes de chaînes étaient stockées dans la génération permanente, ce qui peut provoquer une série de problèmes de performances et d'erreurs de débordement de mémoire. Dans JDK8, seules les références de chaîne sont stockées dans le pool de constantes de chaîne.
Avant que l'action GC ne se produise, il est nécessaire de déterminer quels objets dans la mémoire du tas sont vivants. Il y en a généralement deux. méthodes : comptage de références et méthode d'analyse expressive disponible.
1. Méthode de comptage de références
Ajouter un compteur de référence à l'objet Chaque fois qu'un objet y fait référence, le compteur augmente de 1. Lorsque l'objet est épuisé. , le compteur est décrémenté de 1, et un objet avec une valeur de compteur de 0 indique qu'il ne peut plus être utilisé.
La méthode de comptage de références est simple à mettre en œuvre et efficace dans la détermination, mais elle ne peut pas résoudre le problème des références mutuelles entre objets.
public class GCtest { private Object instance = null; private static final int _10M = 10 * 1 << 20; // 一个对象占10M,方便在GC日志中看出是否被回收 private byte[] bigSize = new byte[_10M]; public static void main(String[] args) { GCtest objA = new GCtest(); GCtest objB = new GCtest(); objA.instance = objB; objB.instance = objA; objA = null; objB = null; System.gc(); } }
En ajoutant -XX : paramètre PrintGC, le résultat en cours d'exécution est :
[GC (System.gc()) [PSYoungGen: 26982K->1194K(75776K)] 26982K->1202K(249344K), 0.0010103 secs]
从GC日志中可以看出objA和objB虽然相互引用,但是它们所占的内存还是被垃圾收集器回收了。
2、可达性分析法
通过一系列称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,搜索路径称为 “引用链”,以下对象可作为GC Roots:
本地变量表中引用的对象
方法区中静态变量引用的对象
方法区中常量引用的对象
Native方法引用的对象
当一个对象到 GC Roots 没有任何引用链时,意味着该对象可以被回收。
在可达性分析法中,判定一个对象objA是否可回收,至少要经历两次标记过程:
1、如果对象objA到 GC Roots没有引用链,则进行第一次标记。
2、如果对象objA重写了finalize()方法,且还未执行过,那么objA会被插入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的Finalizer线程触发其finalize()方法。finalize()方法是对象逃脱死亡的最后机会,GC会对队列中的对象进行第二次标记,如果objA在finalize()方法中与引用链上的任何一个对象建立联系,那么在第二次标记时,objA会被移出“即将回收”集合。
看看具体实现
public class FinalizerTest { public static FinalizerTest object; public void isAlive() { System.out.println("I'm alive"); } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("method finalize is running"); object = this; } public static void main(String[] args) throws Exception { object = new FinalizerTest(); // 第一次执行,finalize方法会自救 object = null; System.gc(); Thread.sleep(500); if (object != null) { object.isAlive(); } else { System.out.println("I'm dead"); } // 第二次执行,finalize方法已经执行过 object = null; System.gc(); Thread.sleep(500); if (object != null) { object.isAlive(); } else { System.out.println("I'm dead"); } } }
执行结果:
method finalize is running I'm alive I'm dead
从执行结果可以看出:
第一次发生GC时,finalize方法的确执行了,并且在被回收之前成功逃脱;
第二次发生GC时,由于finalize方法只会被JVM调用一次,object被回收。
当然了,在实际项目中应该尽量避免使用finalize方法。
Java GC 的那些事(1)
Java GC的那些事(2)
以上就是Java GC 的那些事(1)的内容,更多相关内容请关注PHP中文网(www.php.cn)!