Domanda

vorrei sapere perché il file .o che otteniamo da compilare un file .c che stampa "Ciao, mondo!" è più grande di un file Java .class che stampa anche "Ciao, mondo!"?

È stato utile?

Soluzione

Java utilizza Bytecode per essere indipendente dalla piattaforma e "precompilato", ma bytecode è usato da interprete e viene servita ad essere abbastanza compatto, quindi non è lo stesso che il codice macchina che potete vedere nel programma C compilato. Basta dare uno sguardo al completo processo di compilazione Java:

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

Questa è la catena per il programma Java in codice macchina di trasformazione. Come si vede bytecode è lontano dal codice macchina. Non riesco a trovare nella roba buona Internet visualizzare questa strada sul programma reale (un esempio), tutto quello che ho trovato è questa presentazione , qui si può vedere come ogni passi cambia presentazione codice. Spero che tu come e perché il programma C compilato e bytecode Java sono diverse risposte.

UPDATE: Tutte le fasi che sono dopo "bytecode" sono fatte da JVM in fase di esecuzione a seconda della sua decisione di compilare il codice (questa è un'altra storia ... JVM è bilanciamento tra interpretazione bytecode e la sua compilazione in piattaforma di codice dipendente nativo)

Finalmente trovato il buon esempio, tratto da scansione lineare Registro di assegnazione per l'HotSpot Java ™ Cliente Compiler (btw buona lettura per capire cosa sta succedendo all'interno JVM). Immaginiamo di avere il programma Java:

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

allora il suo bytecode è:

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

ciascun comando richiederà 1 byte (JVM supporta 256 comandi, ma in realtà ha meno di quel numero) + argomenti. Insieme richiede 27 byte. Tralascio tutte le fasi, e qui è pronto per eseguire codice macchina:

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

prende 83 (52 in esadecimale + 1 byte) byte in seguito.

PS. Non prendo in considerazione il collegamento (è stato detto da altri), così come le intestazioni dei file compiledc e bytecode (probabilmente sono troppo diversi, io non so come sia con C, ma nel file di codice byte tutte le stringhe sono spostati pool speciale intestazione, e in programma ci viene utilizzata la sua "posizione" in intestazione, ecc.)

UPDATE2: Probabilmente vale la pena di menzionare, che le opere java con la pila (iStore / Iload comandi), anche se il codice macchina basata su x86 e la maggior parte delle altre opere di piattaforma con i registri. Come si può vedere il codice macchina è "piena" di registri e che dà dimensione in più per il programma compilato nel confronto con più semplice bytecode stack-based.

Altri suggerimenti

La causa principale della differenza di dimensioni in questo caso è differenza di formati di file. Per un tale piccolo formato del programma del file ELF (.o) introduce gravi in ??testa in termini di spazio.

Per esempio, il mio file .o campione della "Ciao, mondo" programma prende 864 byte . Si compone di (esplorato con il comando readelf):

  • 52 byte di intestazione del file
  • 440 bytes di intestazione di sezione (40 byte x 11 sezioni)
  • 81 byte di nomi di sezione
  • 160 byte di tabella dei simboli
  • 43 byte di codice
  • 14 byte di dati (Hello, world\n\0)
  • etc

file .class del programma simile richiede solo 415 byte , nonostante il fatto che essa contiene più nomi di simboli e questi nomi sono lunghi. Si compone di (esplorato con Java Class Viewer ):

  • 289 byte di pool di costanti (include le costanti, i nomi dei simboli, ecc)
  • 94 byte della tabella dei metodi (codice)
  • 8 byte di tabella attributi (nome file sorgente riferimento)
  • 24 byte di header di dimensione fissa

Vedi anche:

programmi in C, anche se sono compilate in codice macchina nativo che gira sul processore (inviati tramite il sistema operativo, ovviamente), tendono ad avere bisogno di fare un sacco di set up e abbattere per il sistema operativo, il caricamento librerie dinamicamente collegate, come la libreria C, ecc.

Java, invece, compila a bytecode per una piattaforma virtuale (sostanzialmente un simulato computer nello-computer), che è specificamente progettato fianco Java stesso, così un sacco di questo overhead (se sarebbe addirittura necessaria poiché sia ??il codice e l'interfaccia VM è ben definito) possono essere spostati nella stessa VM, lasciando il codice programma sia magra.

E 'varia da compilatore a compilatore, però, e ci sono diverse opzioni per ridurre o codice di accumulo in modo diverso, che avrà effetti diversi.

Detto questo, non è poi tanto importante.

In breve: i programmi Java sono compilate a Java byte code, che richiede un interprete separato (Java Virtual Machine) che deve essere eseguito.

non è una garanzia al 100% che il file .o prodotto dal compilatore C è più piccolo, rispetto al file .class prodotto dal compilatore Java. Tutto dipende l'attuazione del compilatore.

Una delle ragioni principali per le differenze nelle dimensioni dei file .o e .class è che bytecode Java sono un più alto livello di bit di istruzioni macchina. Non enormemente di più alto livello, ovviamente - è ancora roba abbastanza di basso livello - ma che farà la differenza, perché agisce in modo efficace per comprimere il tutto . (Sia codice C e Java può avere codice di avvio in là.)

Un'altra differenza è che i file di classe Java rappresentano spesso relativamente piccoli pezzi di funzionalità. Anche se è possibile avere i file oggetto C che mappa a pezzi ancora più piccoli, è spesso più comune per mettere più (legati) funzionalità in un unico file. Le differenze nelle regole di scoping possono anche agire per sottolineare questo (C in realtà non ha nulla che corrisponde al campo di applicazione a livello di modulo, ma ha portata a livello di file, invece, la portata del pacchetto di Java funziona su file di classe multipli). È possibile ottenere una migliore metrica se si confronta la dimensione di un intero programma.

In termini di dimensioni "legati", file eseguibili JAR Java tendono ad essere più piccoli (per un dato livello di funzionalità) perché sono consegnati compressi. E 'relativamente rara per fornire programmi in C in forma compressa. (C'è anche differenze nelle dimensioni della libreria standard, ma potrebbe anche essere un lavaggio perché i programmi C possono contare su librerie diverse da libc essere attuale, e programmi Java avere accesso a una vasta libreria standard. Raccolta a parte che ha il vantaggio è scomodo.)

Poi, c'è anche la questione delle informazioni di debug. In particolare, se si compila un programma C con il debugging che fa IO, si otterrà un sacco di informazioni sui tipi nella libreria standard in dotazione, solo perché è un po 'troppo scomodo per filtrare fuori. Il codice Java avrà solo informazioni di debug sul codice effettivo compilato perché può contare su informazioni pertinenti di essere disponibili nel file oggetto. Questo cambia la dimensione effettiva del codice? No. Ma può avere un grande impatto sulle dimensioni dei file.

Nel complesso, direi che è difficile confrontare le dimensioni dei programmi C e Java. O meglio, è possibile confrontarli e facilmente imparare niente molto utile.

La maggior parte (fino al 90% per le funzioni semplici) di un file .o ELF-formato è spazzatura. Per un file .o contenente un singolo corpo di una funzione vuota, ci si può aspettare una ripartizione dimensioni come:

  • 1% codice
  • 9% simbolo e tabella di rilocazione (essenziale per il collegamento)
  • 90% di testa in testa, inutili versione / note vendor memorizzate dal compilatore e / o assembler, ecc.

Se volete vedere la dimensione reale di codice C compilato, utilizzare il comando size.

Un file di classe è Java bytecode.

E 'più probabile minore dal momento che le librerie C / C ++ e le librerie del sistema operativo sono legati al codice oggetto del compilatore C ++ produce per fare finalmente un binario eseguibile.

In poche parole, è come paragonare Java bytecode in codice oggetto prodotto da un compilatore C prima di essere legato a creare un binario. La differenza è il fatto che una JVM interpreta il codice Java byte correttamente fare ciò che il programma è destinato a fare, mentre C richiede informazioni dal sistema operativo dal momento che le funzioni del sistema operativo come l'interprete.

Anche nel simbolo C Ogni (funzioni, ecc) si fa riferimento da una libreria esterna, almeno una volta in uno dei file oggetto viene importato. Se lo si usa in più file oggetto, è ancora importata solo una volta. Ci sono due modi in cui questo "importazione" può accadere. Con il collegamento statico, il codice vero e proprio di una funzione viene copiato nel file eseguibile. Questa dimensione del file aumenta, ma ha il vantaggio che non sono necessarie librerie esterne (DLL / .so file). Con dinamico che collega questo non accade, ma come risultato il programma richiede librerie aggiuntive per l'esecuzione.

In Java, tutto è "legata" in modo dinamico, per così dire.

Java viene compilato in un linguaggio indipendente macchina. Ciò significa che, dopo che è stato compilato viene poi tradotto in fase di esecuzione da parte del Java Virtual Machine (JVM). C viene compilato per istruzioni macchina ed è quindi tutto il binario per il programma da eseguire sul computer di destinazione.

Poiché Java viene compilato in un linguaggio autonomo macchina, i dettagli specifici per una particolare macchina sono gestite dalla JVM. (Cioè C trovi macchina specifica dall'alto)

E 'così che ci penso in ogni caso: -)

Alcuni potenziali motivi:

  • Il file di classe Java non include il codice di inizializzazione a tutti. E 'solo la tua una classe e una funzione in esso - molto piccolo in effetti. In confronto, il programma C ha un certo grado di codice di inizializzazione collegata in modo statico, e possibilmente thunk DLL.
  • Il programma C può anche avere sezioni allineate a limiti di pagina -. Questo aggiungerebbe un minimo di 4 KB per le dimensioni del programma proprio così, al fine di garantire l'avvio dei segmenti di codice su un limite di pagina
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top