Comprendre parfaitement le processus de JVM du début à la fin

Qu'est-ce que la JVM expérimente du début à la fin ?

1. Charger la classe

Pour une classe Java, elle est d'abord compilée dans un fichier de classe. Lorsque la machine virtuelle exécute une méthode et constate qu'il n'y a pas de représentation binaire de la classe en mémoire, elle essaiera de trouver la représentation binaire de la classe via le chargeur de classe .

Mécanisme de délégation parentale

​ Étant donné le même nom de classe, il peut toujours être chargé dans la même classe. Ceci est réalisé via la machine de délégation parent. Chaque chargeur de classe a sa propre stratégie de chargement. HotSpot a plusieurs chargeurs de classe intégrés. Le répertoire lorsque la classe est chargé est différent. Lorsque le chargeur de classe charge une classe, il transmet d'abord la tâche de chargement de la classe à son chargeur de classe parent, et délègue couche par couche. Si le chargeur de classe parent peut le charger, alors Chargement, si le parent classe ne peut pas être chargée, elle sera transmise à la classe enfant à charger. Pour le dire franchement, lors du chargement d'une classe, la priorité du chargeur de classe parent est supérieure à celle de la classe enfant. Par exemple, si vous écrivez un type String avec le même nom que le type String fourni avec Java, alors le programme chargera définitivement le type String dans la mémoire lorsqu'il démarrera, et il chargera le type String lorsque la classe sera chargée . La tâche est confiée à la classe parente. Pour parler franchement, c'est le chargeur de classe fourni avec Java. Il chargera son propre type String au lieu de l'écrire lui-même.

2. Lien

Ce processus peut être divisé en trois étapes : vérification, préparation et analyse (facultatif)

Vérifier

Cette étape vérifie que la classe chargée est au format correct, a une table de symboles correctement formatée et a une sémantique correcte

Préparer

Les préparatifs incluent l'allocation d'espace pour le stockage statique et les structures de données utilisées à l'intérieur de la machine virtuelle

Analyser (facultatif)

​ Le contenu principal de la phase d'analyse est de charger d'autres classes et interfaces impliquées dans la classe actuelle, et de vérifier si la référence est correcte. Il s'agit d'un processus récursif, similaire au lien "statique" du langage C. Au contraire , il existe également une méthode de liaison plus "paresseuse" - les classes ou les interfaces ne sont résolues que lorsqu'elles sont utilisées.

Les deux méthodes ci-dessus ont leurs propres avantages et inconvénients. La première méthode peut trouver le problème avant l'exécution, tandis que la seconde méthode peut être trouvée au moment de l'exécution.

3. Initialiser (initialiser)

La prémisse de l' initialisation , ce qui est un processus récursif. L'objet le plus simple est la superclasse de toutes les classes, donc cette initialisation récursive se termine par l'objet

Initialiser le verrou

Étant donné que Java est multithread, afin d'éviter les conflits lors du chargement, chaque classe ou interface possède un verrou d'initialisation unique et un état indiquant l'initialisation de cette classe.

  • Validé et prêt, non initialisé
  • est initialisé par un thread
  • a été initialisé et peut être utilisé
  • en état d'erreur, l'initialisation a peut-être échoué

Lorsqu'un thread essaie d'initialiser une classe, il acquiert d'abord le verrou d'initialisation , et après l'avoir acquis, il décide de libérer le verrou et d'attendre ou non en fonction de son état spécifique. Par exemple : cette classe est en cours d'initialisation par un autre thread, puis le thread actuel libère le verrou d'initialisation et entre dans l'état de blocage, jusqu'à ce que le thread en cours d'initialisation soit averti que l'initialisation est terminée.

4. Création de nouvelles instances de classe

Instanciation explicite et instanciation implicite

Afficher l'instanciation :

​ C'est relativement simple, en fait, il est instancié via le constructeur

Instanciation implicite :

​ 1. Le chargement d'une classe ou d'une interface contenant un type String créera implicitement un objet String

2. Opérations de boxe, telles que le type de retour Integer, mais à la fin de la méthode return 0, une classe wrapper sera créée implicitement

3. 字符串 + String类型L'opération créera un nouvel objet String

4. lambda表达式Un objet d'instance qui implémente l'interface peut être créé

processus d'instanciation

​ Le constructeur de la classe sera appelé pendant le processus d'instanciation. Il y a cinq étapes dans le processus de traitement. Parce que c'est plus compliqué, je ne le mentionnerai que brièvement ici, puis je laisserai tout le monde le comprendre à travers des exemples.

​ 1. Lorsque le constructeur est exécuté, si le paramètre est aussi une classe, son constructeur sera exécuté. Par exemple, si le paramètre est de type String, le constructeur de String sera exécuté en premier.

​ 2. Avant d'exécuter la logique spécifique dans le constructeur, le constructeur de la classe parent sera exécuté en premier. Il s'agit d'un processus récursif, ce qui signifie qu'un super()constructeur (sauf Object) est masqué.

3. Lors de l'appel du constructeur de la classe parent, lors de l'appel d'une méthode qui est remplacée par la sous-classe, la sous-classe sera appelée en premier (des exemples seront donnés ci-dessous)

Exemple 1:

class Point {
    
    
    // super(); //隐藏的父类构造方法
    int x, y;
    Point() {
    
     x = 1; y = 1; }
}
class ColoredPoint extends Point {
    
    
    // super(); //隐藏的父类构造方法
    int color = 0xFF00FF;
}
class Test {
    
    
    public static void main(String[] args) {
    
    
        ColoredPoint cp = new ColoredPoint();
        System.out.println(cp.color);
    }
}

Processus de mise en œuvre

ColoredPoint() --> Point() --> Object() --> 初始化x,y --> Point() --> 初始化color变量

Exemple 2 :

class Super {
    
    
    Super() {
    
     printThree(); }
    void printThree() {
    
     System.out.println("three"); }
}
class Test extends Super {
    
    
    int three = (int)Math.PI;  // That is, 3
    void printThree() {
    
     System.out.println(three); }

    public static void main(String[] args) {
    
    
        Test t = new Test();
        t.printThree();
    }
}

sortir:

0
3

Processus de mise en œuvre:

1、执行Test()
2、执行Test()中的super(),也就是Super()方法
3、然后执行Super()方法中的super(),也就是Object的构造方法
4、执行Super类的printThree()方法,因为被子类重写了,所以会调用子类的printThree()方法,输出tree变量的值0(是默认值,因为 int three = (int)Math.PI 还没被执行)
5、执行Test类中的 int three = (int)Math.PI 让three的值变为了3
6、再执行Test类中的printThree()方法,此时输出3

5. Finalisation des instances de classe

L'objet a une méthode protectdécorée finalize, ce qui signifie que n'importe quelle sous-classe peut appeler la finalizeméthode de la superclasse. Lorsqu'un objet est inaccessible (lorsqu'il n'est plus utilisé), il sera recyclé, et avant cela, la machine virtuelle Java appellera cette méthode.

Caractéristiques

1. finalizeLa méthode peut libérer des ressources que la JVM ne peut pas libérer, donc le simple fait de récupérer la mémoire utilisée par l'objet ne récupère pas toutes les ressources qu'il possède

2. Contrairement au constructeur, finalizeles appels ne sont pas ordonnés, c'est-à-dire que lorsqu'un lot d'objets doit être recyclé, leurs finalizeméthodes peuvent être appelées dans n'importe quel ordre, et même plusieurs threads peuvent les appeler simultanément.

3. Contrairement au constructeur, la JVM n'appellera pas automatiquement la finalizeméthode de la classe parente à moins qu'elle ne soit écrite dans le programme.

4. Si finalizeune exception non interceptée est levée dans la méthode, l'exception sera ignorée et finalizel'appel à la méthode sera terminé, par exemple :

public class Test {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        testFinalize();
    }

    private static void testFinalize() throws InterruptedException {
    
    
        List<ObjectA> list = new ArrayList<>(10);
        for (int i = 0; i < 5; i++) {
    
    
            list.add(new ObjectA());
        }
        list = new ArrayList<>();
        System.gc();
        Thread.sleep(Long.parseLong("4000"));
        System.out.println("complete!");
    }
}
class ObjectA {
    
    
    @Override
    protected void finalize() {
    
    
        System.out.println("finalize() start");
        // 执行到这里后,因为异常将不会往下执行
        int a = 10 / 0;
        System.out.println("finalize() end");
    }
}

sortir:

finalize() start
finalize() start
finalize() start
finalize() start
finalize() start
complete!

6. Déchargement des classes et des interfaces

En plus de récupérer les instances d'objet mentionnées ci-dessus, Java peut également recycler les classes et les interfaces, c'est-à-dire "décharger" les classes et les interfaces. Le déchargement des classes n'est pas nécessaire, c'est juste une optimisation qui n'a de sens que pour les applications qui chargent beaucoup de classes au début puis cessent de les utiliser au bout d'un moment.

Caractéristiques

1. Si et seulement si le chargeur de classes est recyclé, les classes et interfaces qu'il charge peuvent être déchargées

2. Les classes et les interfaces chargées par BootStrap Loader ne peuvent pas être déchargées

7. Quitter le programme

La sortie du programme se produit dans les deux situations suivantes :

1. Tous les threads non démons sont terminés

2. Appeler une classe Runtime.getRuntime().exit(n)ou une System.exit(n)méthode

Je suppose que tu aimes

Origine blog.csdn.net/weixin_44829930/article/details/121192093
conseillé
Classement