bytecode Java - Java bytecode
Le bytecode Java est le jeu d'instructions de la machine virtuelle Java (JVM).
Relation avec Java
Un programmeur Java n'a pas du tout besoin de connaître ou de comprendre le bytecode Java. Cependant, comme suggéré dans le journal IBM developerWorks, "Comprendre le bytecode et quel bytecode est susceptible d'être généré par un compilateur Java aide le programmeur Java de la même manière que la connaissance de l' assembly aide le programmeur C ou C++ ."
Architecture d'ensemble d'instructions
La JVM est à la fois une machine à pile et une machine à registre . Chaque trame pour un appel de méthode a une "pile d'opérandes" et un tableau de "variables locales". La pile d'opérandes est utilisée pour les opérandes des calculs et pour recevoir la valeur de retour d'une méthode appelée, tandis que les variables locales ont le même objectif que les registres et sont également utilisées pour transmettre des arguments de méthode. La taille maximale de la pile d'opérandes et du tableau de variables locales, calculée par le compilateur, fait partie des attributs de chaque méthode. Chacun peut être dimensionné indépendamment de 0 à 65535 valeurs, où chaque valeur est de 32 bits. long
et les double
types, qui sont de 64 bits, occupent deux variables locales consécutives (qui n'ont pas besoin d'être alignées sur 64 bits dans le tableau de variables locales) ou une valeur dans la pile d'opérandes (mais sont comptées comme deux unités dans la profondeur de la pile) .
Jeu d'instructions
Chaque bytecode est composé d'un octet qui représente l' opcode , avec zéro ou plusieurs octets pour les opérandes.
Sur les 256 opcodes possibles de long octet , en 2015, 202 sont en cours d'utilisation (~79%), 51 sont réservés pour une utilisation future (~20%), et 3 instructions (~1%) sont réservées en permanence aux implémentations JVM pour utilisation. Deux d'entre eux ( impdep1
et impdep2
) doivent fournir des pièges pour le logiciel et le matériel spécifiques à l'implémentation, respectivement. Le troisième est utilisé par les débogueurs pour implémenter des points d'arrêt.
Les instructions se répartissent en plusieurs grands groupes :
- Charger et stocker (par exemple
aload_0
,istore
) - Arithmétique et logique (par exemple
ladd
,fcmpl
) - Conversion de type (par exemple
i2b
,d2i
) - Création et manipulation d'objets (
new
,putfield
) - Gestion de la pile d'opérandes (par exemple
swap
,dup2
) - Transfert de contrôle (par exemple
ifeq
,goto
) - Invocation et retour de méthode (par exemple
invokespecial
,areturn
)
Il existe également quelques instructions pour un certain nombre de tâches plus spécialisées telles que le lancement d'exceptions, la synchronisation, etc.
De nombreuses instructions ont des préfixes et/ou des suffixes faisant référence aux types d'opérandes sur lesquels elles opèrent. Ceux-ci sont les suivants :
Préfixe suffixe | Type d'opérande |
---|---|
i |
entier |
l |
longue |
s |
court |
b |
octet |
c |
personnage |
f |
flotter |
d |
double |
a |
référence |
Par exemple, iadd
ajoutera deux nombres entiers, tandis que dadd
ajoutera deux doubles. Les instructions const
, load
, et store
peuvent également prendre un suffixe de la forme , où n est un nombre compris entre 0 et 3 pour et . Le n maximum pour diffère selon le type.
_n
load
store
const
Les const
instructions poussent une valeur du type spécifié sur la pile. Par exemple, iconst_5
poussera un entier (valeur 32 bits) avec la valeur 5 sur la pile, tandis que dconst_1
poussera un double (valeur à virgule flottante 64 bits) avec la valeur 1 sur la pile. Il y a aussi un aconst_null
, qui pousse une null
référence. Le n pour les instructions load
et store
spécifie l'index dans le tableau de variables locales à partir duquel charger ou stocker. L' aload_0
instruction pousse l'objet dans la variable locale 0 sur la pile (c'est généralement l' this
objet). istore_1
stocke l'entier en haut de la pile dans la variable locale 1. Pour les variables locales au-delà de 3, le suffixe est supprimé et les opérandes doivent être utilisés.
Exemple
Considérez le code Java suivant :
outer:
for (int i = 2; i < 1000; i++) {
for (int j = 2; j < i; j++) {
if (i % j == 0)
continue outer;
}
System.out.println (i);
}
Un compilateur Java peut traduire le code Java ci-dessus en bytecode comme suit, en supposant que ce qui précède a été mis dans une méthode :
0: iconst_2
1: istore_1
2: iload_1
3: sipush 1000
6: if_icmpge 44
9: iconst_2
10: istore_2
11: iload_2
12: iload_1
13: if_icmpge 31
16: iload_1
17: iload_2
18: irem
19: ifne 25
22: goto 38
25: iinc 2, 1
28: goto 11
31: getstatic #84; // Field java/lang/System.out:Ljava/io/PrintStream;
34: iload_1
35: invokevirtual #85; // Method java/io/PrintStream.println:(I)V
38: iinc 1, 1
41: goto 2
44: return
Génération
Le langage le plus courant ciblant la machine virtuelle Java en produisant du bytecode Java est Java. A l'origine, un seul compilateur existait, le compilateur javac de Sun Microsystems , qui compile le code source Java en bytecode Java ; mais parce que toutes les spécifications pour le bytecode Java sont maintenant disponibles, d'autres parties ont fourni des compilateurs qui produisent du bytecode Java. Voici des exemples d'autres compilateurs :
- Compilateur Eclipse pour Java (ECJ)
- Jikes , compile de Java en bytecode Java (développé par IBM , implémenté en C++ )
- Espresso, compile de Java en bytecode Java (Java 1.0 uniquement)
- Compilateur GNU pour Java (GCJ), compile de Java en bytecode Java ; il peut également compiler en code machine natif et faisait partie de la GNU Compiler Collection (GCC) jusqu'à la version 6.
Certains projets fournissent des assembleurs Java pour permettre l'écriture de bytecode Java à la main. Le code assembleur peut également être généré par machine, par exemple par un compilateur ciblant une machine virtuelle Java . Les assembleurs Java notables incluent :
- Jasmin , prend des descriptions de texte pour les classes Java, écrites dans une syntaxe simple de type assemblage à l'aide du jeu d'instructions de la machine virtuelle Java et génère un fichier de classe Java
- Jamaica, un langage d'assemblage de macros pour la machine virtuelle Java . La syntaxe Java est utilisée pour la définition de classe ou d'interface. Les corps de méthode sont spécifiés à l'aide d'instructions de bytecode.
- Krakatau Bytecode Tools, contient actuellement trois outils : un décompilateur et un désassembleur pour les fichiers de classe Java et un assembleur pour créer des fichiers de classe.
- Lilac, un assembleur et désassembleur pour la machine virtuelle Java .
D'autres ont développé des compilateurs, pour différents langages de programmation, pour cibler la machine virtuelle Java, tels que :
- Fusion froide
- JRuby et Jython , deux langages de script basés sur Ruby et Python
- Apache Groovy , langage à usage général dynamique et typé en option, avec des capacités de typage statique et de compilation statique
- Scala , un langage de programmation à usage général de type sûr prenant en charge la programmation orientée objet et fonctionnelle
- JGNAT et AppletMagic, compilez du langage Ada vers le bytecode Java
- Compilateurs de byte-code C vers Java
- Clojure , un langage de programmation fonctionnel, immuable et polyvalent de la famille Lisp avec un fort accent sur la concurrence
- Kawa , une implémentation du langage de programmation Scheme , également un dialecte de Lisp .
- MIDletPascal
- Le code JavaFX Script est compilé en bytecode Java
- Kotlin , un langage de programmation généraliste à typage statique avec inférence de type
- Le code source Object Pascal est compilé en bytecode Java à l'aide du compilateur Free Pascal 3.0+.
Exécution
Il existe plusieurs machines virtuelles Java disponibles aujourd'hui pour exécuter le bytecode Java, à la fois des produits gratuits et commerciaux. Si l'exécution du bytecode dans une machine virtuelle n'est pas souhaitable, un développeur peut également compiler le code source Java ou le bytecode directement en code machine natif avec des outils tels que le compilateur GNU pour Java (GCJ). Certains processeurs peuvent exécuter nativement le bytecode Java. De tels processeurs sont appelés processeurs Java .
Prise en charge des langues dynamiques
La machine virtuelle Java fournit une certaine prise en charge des langages à typage dynamique . La plupart du jeu d'instructions JVM existant est typé statiquement - dans le sens où les appels de méthode ont leurs signatures vérifiées au moment de la compilation , sans mécanisme pour reporter cette décision à l' exécution , ou pour choisir l'envoi de la méthode par une approche alternative.
JSR 292 ( Prise en charge des langages de type dynamique sur la plate-forme Java ) a ajouté une nouvelle invokedynamic
instruction au niveau de la JVM, pour permettre l'invocation de méthode reposant sur la vérification de type dynamique (au lieu de l' invokevirtual
instruction à vérification de type statique existante ). La machine Da Vinci est un prototype d'implémentation de machine virtuelle qui héberge des extensions JVM visant à prendre en charge les langages dynamiques. Toutes les JVM prenant en charge JSE 7 incluent également l' invokedynamic
opcode.
Voir également
- Listes d'instructions de bytecode Java
- fichier de classe Java
- Liste des langages JVM
- Outils de rétroportage Java
- Compilateurs C vers Java Virtual Machine
- JStik
- Common Intermediate Language (CIL), le rival de Microsoft pour le bytecode Java
- ObjectWeb ASM
- Bibliothèque d'ingénierie de code d'octet
Les références
Liens externes
- Spécification de la machine virtuelle Java d'Oracle
- Langages de programmation pour la machine virtuelle Java
- Bytecode Visualizer – visionneuse et débogueur de bytecode (plugin Eclipse gratuit)
- AdaptJ StackTrace – débogage au niveau du bytecode avec un contrôle total de la pile, des variables locales et du flux d'exécution
- Java Class Unpacker - plugin pour Total Commander, il permet d'ouvrir les fichiers de classe sous forme d'archives compressées et de voir les champs et les méthodes sous forme de fichiers. Le bytecode peut être visualisé sous forme de texte en utilisant F3