2014-06-07 11 views
7

Oggi ho capito una cosa che sembrava strano per me: ho notato che quando io faccio soloPerché l'elaborazione tenta di catturare senza eccezioni lanciate senza rallentare il programma?

try { 
    doSomething(); 
} catch (Exception e) { 
} 

non è più lento a tutti che se io faccio solo

doSomething(); 

Così ho eseguito un test e ha scritto un codice veloce per dimostrare quello che ho visto, il codice in pratica si limita a una funzione chiamata doSomething() un sacco di volte, una volta senza e una volta con try-catch che lo circonda. Quindi, ecco il codice ad esso se si vuole testare voi stessi:

public class Main { 

private static final long LOOPS = 1000000L; 

public static final void main(String[] args) 
{ 
    System.out.println("Loop without try catch: "+loopWithoutTryCatch(LOOPS)); 
    System.out.println("Loop with try catch: "+loopWithTryCatch(LOOPS)); 
} 

public static long loopWithoutTryCatch(long loops) 
{ 
    long startTime = System.currentTimeMillis(); 

    for (long i = 0L; i < loops; i++) 
    { 
     doSomething(); 
    } 

    return System.currentTimeMillis()-startTime; 
} 

public static long loopWithTryCatch(long loops) 
{ 
    long startTime = System.currentTimeMillis(); 

    for (long i = 0L; i < loops; i++) 
    { 
     try { 
      doSomething(); 
     } catch (Exception e) { 
     } 
    } 

    return System.currentTimeMillis()-startTime; 
} 

public static void doSomething() 
{ 
    for (int i = 0; i < 250; i++) 
    { 
     if (i % 3 == 0) 
     { 
      i++; 
     } 
    } 
} 
} 

E ho ricevuto il seguente output:

Loop without try catch: 375 
Loop with try catch: 373 

Sono rimasto sorpreso così ho provato più e più volte, ma ho sempre avuto risultati simili, in entrambi i casi funziona più o meno nello stesso tempo.

E ora la mia domanda è: perché?

io davvero non capisco, per quanto ne so try-catch scrive le risorse prima di utilizzo in una sorta di tabella per poi - se è gettato alcuna eccezione - essere in grado di ripulirlo e riferimento ai valori che aveva prima che si verificasse l'eccezione.

Questo dovrebbe richiedere almeno un po 'di tempo, non dovrebbe? Ho pensato che forse è perché l'esempio casuale che scelgo non lo rappresenta correttamente, e in quel caso specifico in cui l'ho testato non rallenta nulla, ma mi sembra molto improbabile.

Poi ho pensato che forse ci vuole solo una piccola quantità di tempo che si mangia inferme con quella quantità "pochi" delle esecuzioni, così mi sono imbattuto di nuovo il programma di test con un numero totale di 10 milioni di looping, ma quello che ho trovato ho appena provato quello che avevo già trovato: ci vuole più o meno lo stesso tempo per entrambe le esecuzioni.

Quindi c'è qualche spiegazione logica per questo è il caso o solo un comportamento specifico di esempio di try-catch?

Grazie per eventuali chiarimenti in anticipo.

+1

creazione di eccezione con tutte le informazioni di stack è la parte più costosa. Se non lo stai creando e gettandolo, il sovraccarico è così grande. È possibile modificare i test per creare e lanciare per vedere l'impatto. – Jayan

+0

per ogni evenienza: potresti trovare http://openjdk.java.net/projects/code-tools/jmh/ utile per la microbancatura – Jayan

+1

@reechard Anche nell'inglese corretto (che sarebbe inglese, l'inglese originale) è programma , non programma. Il programma è per uno show televisivo. – Styphon

risposta

9

La "lentezza" nei blocchi throw/catch deriva dal processo di lancio e rilevamento dell'eccezione, non nel processo di impostazione di "trap" per loro. Quando si lancia un'eccezione, JVM deve

  • Creare un'istanza di un'eccezione
  • Preparare lo spazio per l'analisi dello stack
  • Popolare la traccia dello stack nello spazio preparato
  • "staccare la spina" la pila verso il basso per la posizione corretta
  • Passare il controllo al gestore di eccezioni.

Quando niente di tutto questo sta accadendo, JVM bastoni semplicemente una nota che un gestore di eccezioni è disponibile a questo livello nello stack, e continua ad eseguire il codice vero e proprio.

Rendere la funzionalità senza penalità era un obiettivo molto importante per i progettisti di linguaggi: i programmatori non dovrebbero essere tenuti a pagare per cose che non usano. Altrimenti, i programmatori sarebbero tentati di saltare la gestione delle eccezioni o tornare ai modi C di utilizzare i codici di stato per salvare alcuni cicli della CPU qua e là, enunciando la fine delle eccezioni come funzionalità.

6

Il bytecode generato dal codice come la tua segue questo schema

1: invoke method 
2: goto 4 
3: store exception in catch block variable 
    // would contain handling code if there was any 
4: return // or whatever comes after the try-catch 

Exception table 
    if an exception of the type in catch block happens from 1 to 3, goto 3 

Quindi, in pratica tutto quello che hai aggiunto con un try-catch è un goto in più se non si verificano eccezioni. In caso contrario, la JVM cercherà l'eccezione nella tabella delle eccezioni e corrisponderà al punto in cui si è verificata. Quindi preparerà il Exception e passerà a qualsiasi indice di istruzioni specificato. Tutta questa operazione è pesante.

+0

Ciao Sotirios. Come hai visto i bycodes? –

+5

@BoratSagdiyev Se si dispone dei file '. Class' compilati, è possibile verificare il codice byte in forma leggibile dall'uomo con l'applicazione' javap'. –

Problemi correlati