Maison >Java >javaDidacticiel >Introduction détaillée à JAVA Virtual Machine (JVM) (6) - moteur d'exécution de bytecode
Lorsque le moteur d'exécution de la JVM exécute du code Java, il dispose généralement de deux options : l'exécution interprétée (exécutée via un interpréteur) et l'exécution compilée (code local généré via un compilateur juste à temps pour l'exécution).
Cadre de pile
Définition :
Le cadre de pile est constitué de données utilisées pour prendre en charge l'appel de méthode et l'exécution de méthode par la machine virtuelle Structure située à l’intérieur de la pile de machines virtuelles.
Fonction :
Du début de l'appel jusqu'à la fin de l'exécution, chaque méthode correspond à un cadre de pile qui est poussé dans la pile de la machine virtuelle et ressorti du processus de pile.
Caractéristiques :
(1) Le cadre de pile comprend la table de variables locales, la pile d'opérandes, etc. Quelle est sa taille nécessaire ? La table des variables locales et la profondeur de la pile d'opérandes sont déterminées au moment de la compilation. Parce que la quantité de mémoire qui doit être allouée à un cadre de pile ne sera pas affectée par les données variables pendant l'exécution du programme.
(2) Partage de données entre deux cadres de pile. Dans le modèle conceptuel, les deux cadres de pile sont complètement indépendants, mais dans la mise en œuvre de la machine virtuelle, certains traitements d'optimisation seront effectués pour chevaucher partiellement les deux cadres de pile. De cette façon, une partie des données peut être partagée lors des appels de méthode, sans avoir besoin de copier et de transmettre des paramètres supplémentaires.
(1) Table de variables locales
La table de variables locales est un ensemble de stockage de valeurs variables espaces Utilisés pour stocker les paramètres de méthode et les variables locales définies dans la méthode.
//方法参数 max(int a,int b)rrree
Les variables locales sont différentes des variables de classe (variables modifiées avec statique)
Les variables de classe ont deux processus d'attribution de valeurs initiales : la phase de préparation (attribution de la valeur initiale du système) et la phase d'initialisation (attribution d'une valeur initiale définie par le programmeur). Peu importe donc si aucune valeur n’est attribuée à la variable de classe lors de la phase d’initialisation, elle a toujours une certaine valeur initiale.
Mais les variables locales sont différentes. Si elles sont définies mais sans valeur initiale, elles ne peuvent pas être utilisées.
(2) Pile d'opérations
Lorsqu'une méthode commence juste son exécution, la pile d'opérandes de cette méthode est vide pendant le processus d'exécution, il y aura diverses instructions de bytecode pour écrire et extraire le contenu de la pile d'opérandes, c'est-à-dire des opérations pop et push.
Par exemple, calculez :
int a;//全局变量 void say(){ int b=0;//局部变量 }
Les deux éléments les plus proches du sommet de la pile d'opérandes sont 2 et 3. Lorsque l'instruction iadd est exécutée, 2 et 3 seront extraits de la pile et ajouté, puis poussez le résultat ajouté 5 sur la pile.
(3) Lien dynamique
Il existe un grand nombre de références de symboles dans le pool constant du fichier Class et des instructions d'appel de méthode dans le bytecode, prenez simplement une référence symbolique à la méthode dans le pool constant en tant que paramètre. Ces références de symboles sont divisées en deux parties :
Résolution statique : converties en références directes lors de la phase de chargement de la classe ou lors de la première utilisation. Lien dynamique : converti en référence directe à chaque exécution.
(4) Adresse de retour
当一个方法开始执行后,只有两种方式可以退出这个方法:正常退出、异常退出。无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行。
当方法正常退出时
调用者的PC计数器作为返回地址。栈帧中一般会保存这个计数器值。
当方法异常退出时
返回地址是要通过异常处理器表来确定的。栈帧中一般不会保存这部分信息。
方法调用
方法调用是确定调用哪一个方法。
(1)解析
对“编译器可知,运行期不可变”的方法进行调用称为解析。符合这种要求的方法主要包括
静态方法,用static修饰的方法私有方法,用private修饰的方法
(2)分派
分派讲解了虚拟机如何确定正确的目标方法。分派分为静态分派和动态分派。讲解静动态分派之前,我们先看个多态的例子。
Human man=new Man();
在这段代码中,Human为静态类型,其在编译期是可知的。Man是实际类型,结果在运行期才可确定,编译期在编译程序的时候并不知道一个对象的实际类型是什么。
静态分派:
所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。它的典型应用是重载。
public class StaticDispatch{ static abstract class Human{ } static class Man extends Human{ } static class Woman extends Human{ } public void say(Human hum){ System.out.println("I am human"); } public void say(Man hum){ System.out.println("I am man"); } public void say(Woman hum){ System.out.println("I am woman"); } public static void main(String[] args){ Human man = new Man(); Human woman = new Woman(); StaticDispatch sr = new StaticDispatch(); sr.say(man); sr.say(woman); } }
运行结果是:
I am human I am human
为什么会产生这个结果呢?
因为编译器在重载时,是通过参数的静态类型而不是实际类型作为判断依据的。在编译阶段,javac编译器会根据参数的静态类型决定使用哪个重载版本,所以两个对say()方法的调用实际为sr.say(Human)。
动态分派:
在运行期根据实际类型确定方法执行版本的分派过程。它的典型应用是重写。
public class DynamicDispatch{ static abstract class Human{ protected abstract void say(); } static class Man extends Human{ @Override protected abstract void say(){ System.out.println("I am man"); } } static class Woman extends Human{ @Override protected abstract void say(){ System.out.println("I am woman "); } } public static void main(String[] args){ Human man = new Man(); Human woman = new Woman(); man.say(); woman.say(); man=new Woman(); man.say(); } }
运行结果:
I am man I am woman I am woman
这似乎才是我们平时敲的java代码。对于方法重写,在运行时才确定调用哪个方法。由于Human的实际类型是man,因此调用的是man的name方法。其余的同理。
动态分派的实现依赖于方法区中的虚方法表,它里面存放着各个方法的实际入口地址。如果某个方法在子类中被重写了,那子类方法表中的地址将会替换为指向子类实现版本的入口地址,否则,指向父类的实现入口。
单分派和多分派:
方法的接收者与方法的参数统称为方法的宗量,根据分派基于多少种宗量,分为单分派和多分派。
在静态分派中,需要调用者的实际类型和方法参数的类型才能确定方法版本,所以其是多分派类型。在动态分派中,已经知道了参数的实际类型,所以此时只需知道方法调用者的实际类型就可以确定出方法版本,所以其是单分派类型。综上,java是一门静态多分派,动态单分派的语言。
字节码解释执行引擎
虚拟机中的字节码解释执行引擎是基于栈的。下面通过一段代码来仔细看一下其解释的执行过程。
public int calc(){ int a = 100; int b = 200; int c = 300; return (a + b) * c; }
第一步:将100入栈。
Étape 2 : Extrayez 100 de la pile d'opérations et stockez-le dans des variables locales. Il en va de même pour les 200 300 suivants.
Étape 3 : Copiez 100 dans la table des variables locales en haut de la pile d'opérandes.
Étape 4 : Copiez 200 dans la table des variables locales en haut de la pile d'opérandes.
Étape 5 : Retirez 100 et 200 de la pile, effectuez une addition entière et enfin repoussez le résultat 300 sur la pile.
Étape 6 : Copiez le troisième nombre 300 de la table des variables locales vers le haut de la pile. L'étape suivante consiste à retirer les deux 300 de la pile, à effectuer une multiplication entière et à placer le résultat final 90 000 sur la pile.
Étape 7 : La méthode se termine et la valeur entière en haut de la pile d'opérandes est renvoyée à l'appelant de cette méthode.
Ce qui précède est une introduction complète à la machine virtuelle JAVA - moteur d'exécution de bytecode. Pour plus de questions connexes, veuillez visiter le site Web PHP chinois : Tutoriel vidéo JAVA<.>
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!