Vous pouvez comprendre que le byte code de vérification à l'aide de ce diagramme, qui est détaillée dans Oracle docs
Vous trouverez que le byte code de vérification n'arrive qu'une fois et pas deux
L'illustration montre le flux de données et de contrôle du langage Java
code source par le compilateur Java, le chargeur de classe et
bytecode verifier et, partant, sur la machine virtuelle Java, qui
contient l'interprète et le système d'exécution. La question importante est de
que la classe Java chargeur et le bytecode verifier font pas de
hypothèses sur la source primaire du bytecode stream--le code
peut venir à partir du système local, ou il peut avoir traversé la moitié
autour de la planète. Le bytecode verifier agit comme une sorte de gardien:
il garantit que le code transmis à l'interprète de Java est dans un état
d'être exécuté, et peut s'exécuter sans crainte d'enfreindre la Java
interprète. Importées de code n'est pas autorisé à exécuter par tous les moyens
jusqu'à ce que après qu'il a passé le vérificateur de tests. Une fois que le vérificateur est
fait, un certain nombre de propriétés importantes sont connus:
- Il n'y a pas d'opérande débordements de pile ou underflows
- Les types des paramètres de toutes les instructions bytecode sont connus pour toujours être correctes
- Champ d'objet accède sont connus pour être légal--privé, public ou protégé
Alors que tous ce contrôle apparaît extrêmement détaillée, par le temps
le bytecode verifier a fait son œuvre, l'interprète de Java peut
procéder, sachant que le code va s'exécuter en toute sécurité. Sachant que ces
propriétés en fait l'interprète de Java beaucoup plus rapide, car il n'a pas
vérifier quoi que ce soit. Il n'y a pas d'opérande type de contrôles et aucune pile
débordement de contrôles. L'interprète peut ainsi fonctionner à pleine vitesse
sans compromettre la fiabilité.
EDIT:-
À Partir D'Oracle Docs Section 5.3.2:
Lorsque la méthode loadClass de la classe loader L est appelé avec l'
nom N d'une classe ou d'interface C pour être chargé, L doit effectuer l'une des
les deux opérations suivantes dans l'ordre de charger C:
- Le chargeur de classe L peut créer un tableau d'octets qui représentent C comme les octets d'un ClassFile structure (§4.1); ensuite, elle doit appeler le
méthode defineClass de la classe ClassLoader. Invoquant defineClass
les causes de la Machine Virtuelle Java pour dériver une classe ou une interface
dénoté par N à l'aide de L dans le tableau d'octets à l'aide de l'algorithme
au §5.3.5.
- Le chargeur de classe L peut déléguer le chargement de C pour la classe loader L'. Ceci est accompli en passant l'argument N
directement ou indirectement à l'invocation d'une méthode sur L'
(en général, la méthode loadClass). Le résultat de l'invocation est
C.
Comme correctement commenté par Holger, en essayant d'expliquer qu'il est plus avec l'aide d'un exemple:
static int factorial(int n)
{
int res;
for (res = 1; n > 0; n--) res = res * n;
return res;
}
L'octet correspondant de code serait
method static int factorial(int), 2 registers, 2 stack slots
0: iconst_1 // push the integer constant 1
1: istore_1 // store it in register 1 (the res variable)
2: iload_0 // push register 0 (the n parameter)
3: ifle 14 // if negative or null, go to PC 14
6: iload_1 // push register 1 (res)
7: iload_0 // push register 0 (n)
8: imul // multiply the two integers at top of stack
9: istore_1 // pop result and store it in register 1
10: iinc 0, -1 // decrement register 0 (n) by 1
11: goto 2 // go to PC 2
14: iload_1 // load register 1 (res)
15: ireturn // return its value to caller
Notez que la plupart des instructions de la JVM sont tapés.
Maintenant, vous devez noter que le bon fonctionnement de la JVM n'est pas garantie, sauf si le code se réunit au moins les conditions suivantes:
- Type de justesse: les arguments de l'un à l'instruction sont toujours de la
types attendus par l'instruction.
- Pas de débordement de pile ou de dépassement de capacité: une instruction de ne jamais pop un argument
hors une pile vide, ni pousse un résultat sur une pile complète (dont la taille est
égale à la durée maximale de la pile de la taille déclarée pour la méthode).
- Code de confinement: le compteur de programme doivent toujours au sein de la
code de la méthode, le début d'un permis de codage d'instruction
(pas de tomber de la fin de la code de la méthode; pas de branches dans la
milieu d'une instruction de codage).
- Registre d'initialisation: une charge à partir d'un registre doit toujours suivi
moins d'un magasin dans ce registre; en d'autres termes, les registres qui ne
correspondent pas aux paramètres de la méthode ne sont pas initialisés sur la méthode
d'entrée, et c'est une erreur de les charger à partir d'un non initialisée registre.
- L'initialisation de l'objet: lorsqu'une instance d'une classe C est créé, un
des méthodes d'initialisation pour la classe C (correspondant à la
les constructeurs de cette classe) doit être appelé avant la classe
instance peut être utilisée.
Le but de byte code de vérification consiste à vérifier ces conditions une fois pour toutes, par l'analyse statique de code octet au moment du chargement. Byte code qui passe verfification peut ensuite être exécuté plus rapidement.
Également de noter que la vérification du code octet but est de changer la verfification énumérés ci-dessus à partir de l'exécution pour les temps de chargement.
L'explication ci-dessus a été prise à partir de bytecode Java vérification: algorithmes et formalizations