Pourquoi sont compilés les fichiers de classe Java plus petits que les fichiers compilés C?

StackOverflow https://stackoverflow.com/questions/4838221

Question

Je voudrais savoir pourquoi le fichier .o que nous recevons de la compilation d'un fichier .c qui imprime « Bonjour, monde! » est plus grand que le fichier Java qui imprime aussi « Bonjour, monde! »?

Était-ce utile?

La solution

Java utilise bytecode pour être indépendant de la plate-forme et « précompilés », mais bytecode est utilisé par interprète et est servi à être assez compact, il est donc pas le même que le code de la machine que vous pouvez voir dans le programme C compilé. Il suffit de jeter un oeil à l'ensemble du processus de compilation Java:

Java program  
-> Bytecode   
  -> High-level Intermediate Representation (HIR)   
    -> Middle-level Intermediate Representation (MIR)   
      -> Low-level Intermediate Representation (LIR)  
        -> Register allocation
          -> EMIT (Machine Code)

est la chaîne pour le programme Java pour la transformation de code machine. Comme vous le voyez bytecode est loin de code machine. Je ne trouve pas dans les choses de bonne Internet pour vous montrer cette route sur le programme réel (un exemple), tout ce que j'ai trouvé est cette de présentation, ici vous pouvez voir comment chaque étape change la présentation du code. Je l'espère vous répond comment et pourquoi programme C compilé et bytecode Java sont différentes.

Mise à jour: Toutes les étapes qui sont après « bytecode » sont effectuées par machine virtuelle Java dans l'exécution en fonction de sa décision de compiler ce code (qui est une autre histoire ... JVM est en équilibre entre l'interprétation de bytecode et son compilation en code dépendant de la plate-forme native)

Enfin trouvé bon exemple, tiré de Balayage linéaire pour le registre d'attribution Java HotSpot ™ compilateur de client (BTW bonne lecture pour comprendre ce qui se passe à l'intérieur JVM). Imaginez que nous avons programme java:

public static void fibonacci() {
  int lo = 0;
  int hi = 1;
  while (hi < 10000) {
    hi = hi + lo;
    lo = hi - lo;
    print(lo);
  }
}

alors son bytecode est:

0:  iconst_0
1:  istore_0 // lo = 0
2:  iconst_1
3:  istore_1 // hi = 1
4:  iload_1
5:  sipush 10000
8:  if_icmpge 26 // while (hi < 10000)
11: iload_1
12: iload_0
13: iadd
14: istore_1 // hi = hi + lo
15: iload_1
16: iload_0
17: isub
18: istore_0 // lo = hi - lo
19: iload_0
20: invokestatic #12 // print(lo)
23: goto 4 // end of while-loop
26: return

chaque commande prend 1 octet (JVM supporte les commandes 256, mais en fait, a moins que le nombre) + arguments. Ensemble, il faut 27 octets. Je laisse de côté toutes les étapes, et voici prêt à exécuter le code de la machine:

00000000: mov dword ptr [esp-3000h], eax
00000007: push ebp
00000008: mov ebp, esp
0000000a: sub esp, 18h
0000000d: mov esi, 1h
00000012: mov edi, 0h
00000017: nop
00000018: cmp esi, 2710h
0000001e: jge 00000049
00000024: add esi, edi
00000026: mov ebx, esi
00000028: sub ebx, edi
0000002a: mov dword ptr [esp], ebx
0000002d: mov dword ptr [ebp-8h], ebx
00000030: mov dword ptr [ebp-4h], esi
00000033: call 00a50d40
00000038: mov esi, dword ptr [ebp-4h]
0000003b: mov edi, dword ptr [ebp-8h]
0000003e: test dword ptr [370000h], eax
00000044: jmp 00000018
00000049: mov esp, ebp
0000004b: pop ebp
0000004c: test dword ptr [370000h], eax
00000052: ret

Il faut environ 83 (52 en hexadécimal + 1 octet) octets dans la suite.

PS. Je ne prends pas en compte la liaison (a été mentionné par d'autres), ainsi que compiledc et en-têtes de fichier bytecode (probablement ils sont différents aussi, je ne sais pas comment est-il avec c, mais dans le fichier bytecode toutes les chaînes sont déplacées à piscine d'en-tête spécial, et dans le programme, il est utilisé sa « position » en-tête, etc.)

MAJ2: Probablement la peine de mentionner, que les travaux de java avec pile (iStore / icharge commandes), bien que le code machine basé sur x86 et la plupart des autres travaux de plate-forme avec les registres. Comme vous pouvez le voir la machine est « complète » des registres et que la taille supplémentaire pour donne le programme compilé en comparant avec bytecode à base de pile plus simple.

Autres conseils

programmes C, même si elles sont compilées en code machine natif qui fonctionne sur votre processeur (envoyés par le système d'exploitation, bien sûr), ont tendance à besoin de faire beaucoup de mettre en place et au démontage du système d'exploitation, le chargement bibliothèques liées dynamiquement comme la bibliothèque de C, etc.

Java, d'autre part, compile en bytecode pour une plate-forme virtuelle (essentiellement simulé ordinateur-dans un ordinateur), qui est spécialement conçu à côté Java lui-même, donc beaucoup de cette surcharge (si elle serait même nécessaire étant donné que le code et l'interface VM est bien définie) peuvent être déplacés dans la machine virtuelle elle-même, en laissant le code de programme pour être maigre.

Il varie de compilateur à compilateur, cependant, et il y a plusieurs options pour le réduire ou le code de construction différemment, ce qui aura des effets différents.

Tout cela dit, ce n'est pas vraiment important.

En bref: les programmes Java sont compilés à byte code Java, ce qui nécessite un interpréteur séparé (Java Virtual Machine) à exécuter.

Il n'y a pas une garantie à 100% que le fichier .o produit par le compilateur C est plus petit, que le fichier .class produit par le compilateur Java. Tout dépend de la mise en œuvre du compilateur.

L'une des principales raisons des différences dans la taille des fichiers .o et .class est que le bytecode Java sont un peu plus élevé que le niveau des instructions de la machine. Pas énormément de niveau supérieur, bien sûr - il est encore des choses assez bas niveau - mais cela fera une différence parce qu'il agit efficacement pour comprimer le tout programme. (Les deux codes C et Java peut avoir le code de démarrage à l'intérieur.)

Une autre différence est que les fichiers de classe Java représentent souvent des morceaux relativement faibles de fonctionnalité. Bien qu'il soit possible d'avoir des fichiers C d'objet carte pour encore plus petits morceaux, il est souvent plus fréquent de mettre des fonctionnalités plus (liées) dans un seul fichier. Les différences dans les règles de cadrage peuvent également agir pour mettre l'accent sur ce (C n'a pas vraiment tout ce qui correspond à la portée au niveau du module, mais il a une portée de niveau fichier à la place, le champ de package Java fonctionne dans les fichiers de classe multiples). Vous obtenez une meilleure mesure si l'on compare la taille d'un programme.

En termes de « liées » tailles, les fichiers JAR exécutable Java ont tendance à être plus petits (pour un niveau donné de fonctionnalité), car ils sont livrés compressés. Il est relativement rare d'offrir des programmes C sous forme comprimée. (Il y a aussi des différences dans la taille de la bibliothèque standard, mais ils pourraient aussi bien être un lavage, car les programmes C peuvent compter sur les bibliothèques autres que libc être présent, et les programmes Java ont accès à une vaste bibliothèque standard. Décortiquer qui a l'avantage est gênant.)

Ensuite, il y a aussi la question des informations de débogage. En particulier, si vous compilez un programme C avec le débogage sur ce fait IO, vous obtiendrez beaucoup d'informations sur les types dans la bibliothèque standard inclus, juste parce qu'il est un peu trop maladroit pour filtrer dehors. Le code Java aura seulement des informations de débogage sur le code compilé réel, car il peut compter sur les informations pertinentes soient disponibles dans le fichier objet. Est-ce que cela change la taille réelle du code? Non, mais il peut avoir un impact important sur la taille des fichiers.

Dans l'ensemble, je suppose qu'il est difficile de comparer les tailles des programmes C et Java. Ou plutôt, vous pouvez les comparer et facilement apprendre rien de bien utile.

La plupart (jusqu'à 90% pour les fonctions simples) d'un fichier ELF .o format est indésirable. Pour un fichier .o contenant un seul corps de la fonction vide, vous pouvez vous attendre une rupture de taille comme:

  • 1 code%
  • symbole 9% et une table de déplacement (indispensable pour relier)
  • 90% de frais généraux d'en-tête, version inutiles / vendor notes stockées par le compilateur et / ou assembleur, etc.

Si vous voulez voir la taille réelle du code C compilé, utilisez la commande size.

Un fichier de classe est byte code Java.

Il est très probablement plus faible puisque les bibliothèques C / C ++ et les bibliothèques du système d'exploitation sont liées au code objet le compilateur C ++ produit pour finalement faire un binaire exécutable.

En termes simples, il est comme comparer byte code Java en code objet produit par un compilateur C avant qu'il est lié à créer un binaire. La différence réside dans le fait qu'une machine virtuelle Java interprète le code octet Java pour faire correctement ce que le programme est censé faire alors que C nécessite des informations provenant du système d'exploitation étant donné que les fonctions du système d'exploitation que l'interprète.

également dans le symbole C Chaque (fonctions, etc.), vous faites référence à partir d'une bibliothèque externe au moins une fois dans l'un des fichiers d'objet est importé. Si vous l'utilisez dans plusieurs fichiers d'objets, il est encore importé juste une fois. Il y a deux façons cette « importation » peut se produire. Avec la liaison statique, le code réel pour une fonction est copié dans le fichier exécutable. Cette taille du fichier augmente, mais a l'avantage de ne pas les bibliothèques externes (.dll / .so fichiers) sont nécessaires. Avec dynamique liant cela ne se produit pas, mais à la suite de votre programme nécessite des bibliothèques supplémentaires pour exécuter.

En Java, tout est "lié" dynamique, pour ainsi dire.

Java est compilé dans une machine indépendante du langage. Cela signifie que après sa compilation, il est ensuite traduit à l'exécution par la machine virtuelle Java (JVM). C est compilé aux instructions de la machine et est donc tous les binaires pour le programme à exécuter sur la machine cible.

Parce que Java est compilé sur une machine indépendante du langage, les détails spécifiques à une machine particulière sont traités par la machine virtuelle Java. (Par exemple C a de tête spécifique de la machine)

Voilà comment je pense quand même: -)

Quelques raisons possibles:

  • Le fichier de classe Java ne comprend pas le code d'initialisation du tout. Il a juste votre une classe et une fonction en elle - très petit effet. En comparaison, le programme C a un certain degré de code d'initialisation lié statiquement, et peut-être thunks DLL.
  • Le programme C peut également avoir des sections alignées sur les limites de page -. Cela ajouterait un minimum de 4 Ko à la taille du programme comme ça, afin d'assurer le segment de code commence sur une limite de page
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top