Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
public:mco-2:java_bytecode_et_jvm_et_reciproquement [2016/02/04 09:43] – fbrucker | public:mco-2:java_bytecode_et_jvm_et_reciproquement [2016/02/05 11:33] (Version actuelle) – fbrucker | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
+ | ====== Java, bytecode et JVM (et réciproquement) ====== | ||
+ | Nous n' | ||
+ | |||
+ | ===== Prérequis ===== | ||
+ | |||
+ | * Vous avez suivi la partie [[public: | ||
+ | * vous connaissez la différence entre [[http:// | ||
+ | * pour les exemples on supposera que l'on a accès à un terminal (il en existe sous linux, mac et Windows) | ||
+ | |||
+ | ===== Principe ===== | ||
+ | |||
+ | Cette [[https:// | ||
+ | |||
+ | En gros, il existe 3 grand paradigmes pour l' | ||
+ | * **interprétation** : un fichier écrit par un humain est " | ||
+ | * **compilation** : un fichier écrit par un humain est tout d' | ||
+ | * **machine virtuelle** : un fichier écrit par un humain est tout d' | ||
+ | |||
+ | L' | ||
+ | * **compilation** : il faut compiler chaque programme pour toutes les architectures et systèmes d' | ||
+ | * **interprétation** : comme chaque ligne est lue puis compilée puis exécutée, c'est comparativement lent par rapport à un programme compilé. Le byte code de java est un langage intermédiaire plus simple à convertir en langage machine. | ||
+ | |||
+ | ==== Interprété ==== | ||
+ | |||
+ | Programme python '' | ||
+ | <code python> | ||
+ | print(" | ||
+ | </ | ||
+ | |||
+ | On l' | ||
+ | <code terminal> | ||
+ | ~ $ python3 hello.py | ||
+ | Hello World! | ||
+ | ~ $ | ||
+ | </ | ||
+ | |||
+ | ==== Compilé ==== | ||
+ | |||
+ | Programme C " | ||
+ | <code C> | ||
+ | #include < | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | printf(" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | On le compile en exécutable (ici mac) avec un compilateur, | ||
+ | <code terminal> | ||
+ | ~ $ gcc hello.c | ||
+ | ~ $ | ||
+ | </ | ||
+ | |||
+ | Le compilateur a créé un fichier qui s' | ||
+ | <code terminal> | ||
+ | ~ $ file a.out | ||
+ | a.out: Mach-O 64-bit executable x86_64 | ||
+ | ~ $ | ||
+ | </ | ||
+ | |||
+ | Ce fichier peut donc être exécuté sur notre machine : | ||
+ | <code terminal> | ||
+ | ~ $ ./ | ||
+ | hello world! | ||
+ | ~ $ </ | ||
+ | |||
+ | ==== Bytecode Java ==== | ||
+ | |||
+ | Fichier '' | ||
+ | <code java> | ||
+ | public class Hello { | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | On le transforme en bytecode grâce à la commande '' | ||
+ | <code terminal> | ||
+ | ~ $ javac Hello.java | ||
+ | ~ $ | ||
+ | </ | ||
+ | C'est un fichier java // | ||
+ | <code terminal> | ||
+ | ~ $ file Hello.class | ||
+ | Hello.class: | ||
+ | ~ $ | ||
+ | </ | ||
+ | |||
+ | On peut exécuter la classe Hello via la JVM (par défaut, la commande '' | ||
+ | <code terminal> | ||
+ | ~ $ java Hello | ||
+ | Hello World! | ||
+ | ~ $ | ||
+ | </ | ||
+ | |||
+ | ===== Distribuer son code Java ===== | ||
+ | |||
+ | Lorsque l'on a beaucoup de fichiers, resources, etc, et ça va arriver très vite, on a coutume de : | ||
+ | * placer son code dans des répertoires différents via des [[[[public: | ||
+ | * distribuer son code via une archive [[https:// | ||
+ | |||
+ | Pour la démonstration on aura un unique fichier '' | ||
+ | <code java> | ||
+ | package com.mco; | ||
+ | |||
+ | public class Hello { | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | On compile le java : | ||
+ | <code terminal> | ||
+ | ~ $ javac com/ | ||
+ | ~ $ | ||
+ | </ | ||
+ | |||
+ | On execute le bytecode depus le répertoire racine du projet : | ||
+ | <code terminal> | ||
+ | ~ $ java com/ | ||
+ | Hello World! | ||
+ | ~ $ | ||
+ | </ | ||
+ | |||
+ | En supposant que l'on ait beaucoup de fichiers classes on les regroupe en un unique fichier jar. Souvent l'IDE le fera pour vous mais comme on est là pour apprendre on le fait à la main en suivant l'aide de la [[https:// | ||
+ | |||
+ | <code terminal> | ||
+ | jar cfe hello.jar com.mco.Hello com/ | ||
+ | </ | ||
+ | |||
+ | Un jar n'est rien d' | ||
+ | <code terminal> | ||
+ | ~ $ jar tf hello.jar | ||
+ | META-INF/ | ||
+ | META-INF/ | ||
+ | com/ | ||
+ | ~ $ | ||
+ | </ | ||
+ | |||
+ | On peut ensuite l' | ||
+ | <code termnial> | ||
+ | ~ $ java -jar hello.jar | ||
+ | Hello World! | ||
+ | ~ $ | ||
+ | </ | ||
+ | |||
+ | Ou, si l'on veut exécuter une classe particulière du jar : | ||
+ | <code terminal> | ||
+ | ~ $ java -cp hello.jar com.mco.Hello | ||
+ | Hello World! | ||
+ | ~ $ | ||
+ | </ | ||
+ | |||
+ | ===== Voir du bytecode en vrai ===== | ||
+ | |||
+ | On se basera sur l' | ||
+ | |||
+ | Pour l' | ||
+ | |||
+ | |||
+ | Le bytecode se voit en utilisant la commande '' | ||
+ | <code termina> | ||
+ | ~ $ javap -c Hello.class | ||
+ | Compiled from " | ||
+ | public class Hello { | ||
+ | public Hello(); | ||
+ | Code: | ||
+ | 0: aload_0 | ||
+ | 1: invokespecial #1 // Method java/ | ||
+ | 4: return | ||
+ | |||
+ | public static void main(java.lang.String[]); | ||
+ | Code: | ||
+ | 0: getstatic | ||
+ | 3: ldc # | ||
+ | 5: invokevirtual #4 // Method java/ | ||
+ | 8: return | ||
+ | } | ||
+ | ~ $ | ||
+ | </ | ||
+ | |||
+ | Nous avons 7 instructions et le fichier pèse 416 octet. C'est beaucoup plus que le fichier initial qui pèse lui environ 110 octets. Regardons ces 416 octets. | ||
+ | |||
+ | < | ||
+ | Pour voir un fichier exécutable, | ||
+ | |||
+ | Pour cela on utilisera un éditeur permettant de voir un fichier sous forme de suite de nombres. On utilise les [[https:// | ||
+ | |||
+ | Il en existe [[https:// | ||
+ | </ | ||
+ | |||
+ | Le fichier '' | ||
+ | <code terminal> | ||
+ | ~ $ hexdump Hello.class -C | ||
+ | 00000000 | ||
+ | 00000010 | ||
+ | 00000020 | ||
+ | 00000030 | ||
+ | 00000040 | ||
+ | 00000050 | ||
+ | 00000060 | ||
+ | 00000070 | ||
+ | 00000080 | ||
+ | 00000090 | ||
+ | 000000a0 | ||
+ | 000000b0 | ||
+ | 000000c0 | ||
+ | 000000d0 | ||
+ | 000000e0 | ||
+ | 000000f0 | ||
+ | 00000100 | ||
+ | 00000110 | ||
+ | 00000120 | ||
+ | 00000130 | ||
+ | 00000140 | ||
+ | 00000150 | ||
+ | 00000160 | ||
+ | 00000170 | ||
+ | 00000180 | ||
+ | 00000190 | ||
+ | 000001a0 | ||
+ | ~ $ ls -la | ||
+ | </ | ||
+ | |||
+ | La différence de taille s' | ||
+ | |||
+ | ===== Recompiler en Java ===== | ||
+ | |||
+ | Pour recompiler en java un fichier class il faut passer du bytecode au java. Cela ne peut pas se faire sans perte. Pour cela il nous faudra un désassembleur. Nous utiliserons [[http:// | ||
+ | |||
+ | <code terminal> | ||
+ | ~ $ java -jar cfr_0_110.jar Hello.class | ||
+ | /* | ||
+ | * Decompiled with CFR 0_110. | ||
+ | */ | ||
+ | import java.io.PrintStream; | ||
+ | |||
+ | public class Hello { | ||
+ | public static void main(String[] arrstring) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | ~ $ | ||
+ | </ |