personalizzate - try catch throw java




Quanto lente sono le eccezioni Java? (12)

Aleksey Shipilëv ha effettuato un'analisi molto approfondita in cui ha confrontato le eccezioni Java in varie combinazioni di condizioni:

  • Eccezioni appena create rispetto alle eccezioni pre-create
  • Traccia stack abilitata vs disabilitata
  • Stack traccia richiesta vs mai richiesta
  • Catturato al livello più alto contro rethrown ad ogni livello contro incatenato / incartato ad ogni livello
  • Vari livelli di profondità dello stack delle chiamate Java
  • Nessuna ottimizzazione ottimizzata rispetto alle impostazioni di inlining vs default
  • Campi definiti dall'utente letti vs non letti

Inoltre li confronta con le prestazioni di controllo di un codice di errore a vari livelli di frequenza di errore.

Le conclusioni (citate alla lettera dal suo post) erano:

  1. Eccezioni veramente eccezionali sono magnificamente performanti. Se li si utilizza come progettato e si comunicano solo i casi veramente eccezionali tra il numero enorme di casi non eccezionali gestiti da codice normale, l'utilizzo di eccezioni è la vittoria delle prestazioni.

  2. I costi delle prestazioni delle eccezioni hanno due componenti principali: stack trace construction quando viene istanziata l'eccezione e lo stack si sgancia durante il lancio di eccezioni.

  3. I costi di costruzione della traccia dello stack sono proporzionali alla profondità dello stack al momento dell'istanza dell'istanza. Questo è già sbagliato perché chi conosce la profondità di stack in base alla quale verrà chiamato questo metodo di lancio? Anche se si disattiva la generazione traccia stack e / o si memorizzano nella cache le eccezioni, è possibile eliminare solo questa parte del costo delle prestazioni.

  4. I costi di srotolamento dello stack dipendono dalla nostra fortuna nel portare il gestore delle eccezioni più vicino nel codice compilato. Strutturare attentamente il codice per evitare la ricerca di gestori di eccezioni profonde probabilmente ci aiuterà a diventare più fortunati.

  5. Se eliminiamo entrambi gli effetti, il costo delle prestazioni delle eccezioni è quello della filiale locale. Non importa quanto sia bello, ciò non significa che dovresti usare Eccezioni come il solito flusso di controllo, perché in questo caso sei in balia dell'ottimizzazione del compilatore! Dovresti usarli solo in casi veramente eccezionali, dove la frequenza delle eccezioni ammortizza il possibile costo sfortunato di sollevare l'eccezione reale.

  6. La regola empirica ottimistica sembra essere una frequenza di 10 ^ -4 per le eccezioni che è abbastanza eccezionale. Questo, ovviamente, dipende dai pesi pesanti delle eccezioni stesse, dalle azioni esatte intraprese nei gestori di eccezioni, ecc.

Il risultato è che quando un'eccezione non viene lanciata, non si paga un costo, quindi quando la condizione eccezionale è sufficientemente rara, la gestione delle eccezioni è più veloce dell'uso di una if ogni volta. Il post completo merita davvero una lettura.

Domanda: la gestione delle eccezioni in Java è effettivamente lenta?

La saggezza convenzionale, così come molti risultati di Google, afferma che la logica eccezionale non dovrebbe essere utilizzata per il normale flusso di programma in Java. Di solito vengono dati due motivi,

  1. è molto lento - anche un ordine di grandezza più lento del normale codice (i motivi forniti variano),

e

  1. è disordinato perché la gente si aspetta che vengano gestiti solo gli errori in un codice eccezionale.

Questa domanda è circa # 1.

Ad esempio, questa pagina descrive la gestione delle eccezioni Java come "molto lenta" e mette in relazione la lentezza con la creazione della stringa del messaggio di eccezione: "questa stringa viene quindi utilizzata per creare l'oggetto eccezione che viene lanciato." L'articolo Effective Exception Handling in Java afferma che "la ragione di ciò è dovuta all'aspetto della creazione di oggetti nella gestione delle eccezioni, il che rende quindi lenta eccezioni di lancio intrinsecamente lente". Un altro motivo è che la generazione della traccia dello stack è ciò che lo rallenta.

Il mio test (utilizzando Java 1.6.0_07, Java HotSpot 10.0, su Linux a 32 bit) indica che la gestione delle eccezioni non è più lenta del codice normale. Ho provato a eseguire un metodo in un ciclo che esegue del codice. Alla fine del metodo, utilizzo un booleano per indicare se restituire o lanciare . In questo modo l'elaborazione effettiva è la stessa. Ho provato a eseguire i metodi in diversi ordini e ad avere una media dei tempi di test, pensando che potesse essere il riscaldamento della JVM. In tutti i miei test, il lancio è stato almeno veloce quanto il rendimento, se non più veloce (fino al 3,1% più veloce). Sono completamente aperto alla possibilità che i miei test siano sbagliati, ma non ho visto nulla di simile nel codice dell'esempio di codice, nei confronti dei test o nei risultati negli ultimi due anni che mostrano la gestione delle eccezioni in Java per essere effettivamente lento.

Ciò che mi porta su questo percorso è stata una API che dovevo usare che ha gettato le eccezioni come parte della normale logica di controllo. Volevo correggerli nel loro utilizzo, ma ora potrei non essere in grado di farlo. Dovrei invece lodarli per il loro pensiero in avanti?

Nel documento Efficiente gestione delle eccezioni Java nella compilazione just-in-time , gli autori suggeriscono che la presenza di gestori di eccezioni da sola, anche se non vengono lanciate eccezioni, è sufficiente per impedire al compilatore JIT di ottimizzare correttamente il codice, rallentandolo . Non ho ancora testato questa teoria.


Anche se lanciare un'eccezione non è lenta, è comunque una cattiva idea gettare eccezioni per il normale flusso del programma. Utilizzato in questo modo è analogo a un GOTO ...

Immagino che in realtà non risponda alla domanda. Immagino che la saggezza "convenzionale" di rallentare le eccezioni fosse vera nelle versioni java precedenti (<1.4). La creazione di un'eccezione richiede che la VM crea l'intera traccia dello stack. Molto è cambiato da allora nella VM per accelerare le cose e questa è probabilmente un'area che è stata migliorata.


Dipende da come vengono implementate le eccezioni. Il modo più semplice è usare setjmp e longjmp. Ciò significa che tutti i registri della CPU vengono scritti nello stack (che richiede già un po 'di tempo) e probabilmente è necessario creare altri dati ... tutto questo già accade nella dichiarazione try. L'istruzione throw deve svolgere lo stack e ripristinare i valori di tutti i registri (e possibili altri valori nella VM). Quindi provare e lanciare sono ugualmente lenti, e questo è piuttosto lento, tuttavia se non viene lanciata alcuna eccezione, l'uscita dal blocco try non richiede tempo nella maggior parte dei casi (dato che tutto viene messo in pila e si ripulisce automaticamente se il metodo esiste).

Sun e altri hanno riconosciuto che questo è probabilmente non ottimale e, naturalmente, le macchine virtuali diventano sempre più veloci nel tempo. C'è un altro modo per implementare le eccezioni, il che rende la prova lampo veloce (in realtà non succede nulla per provare in generale - tutto ciò che deve accadere è già fatto quando la classe è caricata dalla VM) e rende il lancio non così lento . Non so quale JVM usi questa nuova, migliore tecnica ...

... ma stai scrivendo in Java, quindi il tuo codice verrà eseguito solo su una JVM su un sistema specifico? Dal momento che se può mai funzionare su qualsiasi altra piattaforma o qualsiasi altra versione di JVM (forse di qualsiasi altro fornitore), chi dice che usano anche l'implementazione veloce? Quello veloce è più complicato di quello lento e non è facilmente possibile su tutti i sistemi. Vuoi rimanere portatile? Quindi non fare affidamento sulle eccezioni essendo veloce.

Fa anche una grande differenza quello che fai all'interno di un blocco try. Se apri un blocco try e non invii mai alcun metodo da questo blocco try, il blocco try sarà ultra veloce, in quanto il JIT può quindi effettivamente trattare un lancio come un semplice goto. Non ha nemmeno bisogno di salvare lo stack-state né ha bisogno di srotolare lo stack se viene lanciata un'eccezione (deve solo saltare ai catch catcher). Tuttavia, questo non è quello che fai di solito. Di solito apri un blocco try e poi chiami un metodo che potrebbe generare un'eccezione, giusto? E anche se usi semplicemente il blocco try nel tuo metodo, che tipo di metodo sarà, che non chiama nessun altro metodo? Calcolerà solo un numero? Allora per cosa hai bisogno di eccezioni? Esistono modi molto più eleganti per regolare il flusso del programma. Per praticamente qualsiasi altra cosa, ma semplice matematica, dovrai chiamare un metodo esterno e questo già distrugge il vantaggio di un blocco try locale.

Vedi il seguente codice di prova:

public class Test {
    int value;


    public int getValue() {
        return value;
    }

    public void reset() {
        value = 0;
    }

    // Calculates without exception
    public void method1(int i) {
        value = ((value + i) / i) << 1;
        // Will never be true
        if ((i & 0xFFFFFFF) == 1000000000) {
            System.out.println("You'll never see this!");
        }
    }

    // Could in theory throw one, but never will
    public void method2(int i) throws Exception {
        value = ((value + i) / i) << 1;
        // Will never be true
        if ((i & 0xFFFFFFF) == 1000000000) {
            throw new Exception();
        }
    }

    // This one will regularly throw one
    public void method3(int i) throws Exception {
        value = ((value + i) / i) << 1;
        // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
        // an AND operation between two integers. The size of the number plays
        // no role. AND on 32 BIT always ANDs all 32 bits
        if ((i & 0x1) == 1) {
            throw new Exception();
        }
    }

    public static void main(String[] args) {
        int i;
        long l;
        Test t = new Test();

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            t.method1(i);
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method1 took " + l + " ms, result was " + t.getValue()
        );

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            try {
                t.method2(i);
            } catch (Exception e) {
                System.out.println("You'll never see this!");
            }
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method2 took " + l + " ms, result was " + t.getValue()
        );

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            try {
                t.method3(i);
            } catch (Exception e) {
                // Do nothing here, as we will get here
            }
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method3 took " + l + " ms, result was " + t.getValue()
        );
    }
}

Risultato:

method1 took 972 ms, result was 2
method2 took 1003 ms, result was 2
method3 took 66716 ms, result was 2

Il rallentamento dal blocco try è troppo piccolo per escludere fattori di confondimento come i processi in background. Ma il blocco catch ha ucciso tutto e reso 66 volte più lento!

Come ho detto, il risultato non sarà così male se metti try / catch e getti tutti nello stesso metodo (metodo 3), ma questa è una ottimizzazione JIT speciale su cui non fare affidamento. E anche quando si utilizza questa ottimizzazione, il lancio è ancora piuttosto lento. Quindi non so cosa stai cercando di fare qui, ma c'è sicuramente un modo migliore di farlo rispetto all'utilizzo di try / catch / throw.


Ho effettuato alcuni test delle prestazioni con JVM 1.5 e l'utilizzo di eccezioni è stato almeno 2x più lento. In media: tempo di esecuzione su un metodo banalmente piccolo più che triplicato (3x) con eccezioni. Un loop insignificante che ha dovuto catturare l'eccezione ha visto un aumento 2x in auto-time.

Ho visto numeri simili nel codice di produzione e micro benchmark.

Le eccezioni dovrebbero assolutamente NON essere usate per qualcosa che viene chiamato frequentemente. Lanciare migliaia di eccezioni al secondo causerebbe un enorme collo di bottiglia.

Ad esempio, utilizzando "Integer.ParseInt (...)" per trovare tutti i valori non validi in un file di testo di grandi dimensioni, è una pessima idea. (Ho visto questo metodo di utilità uccidere le prestazioni sul codice di produzione)

Utilizzando un'eccezione per segnalare un valore errato su un modulo della GUI utente, probabilmente non così male dal punto di vista delle prestazioni.

Indipendentemente dal fatto che sia una buona pratica di progettazione, vorrei andare con la regola: se l'errore è normale / previsto, quindi utilizzare un valore di ritorno. Se è anormale, usa un'eccezione. Ad esempio: leggendo gli input dell'utente, i valori errati sono normali - utilizza un codice di errore. Passando un valore a una funzione di utilità interna, i valori errati devono essere filtrati chiamando il codice: utilizzare un'eccezione.


HotSpot è in grado di rimuovere il codice di eccezione per le eccezioni generate dal sistema, purché sia ​​tutto in linea. Tuttavia, eccezioni create in modo esplicito e quelle altrimenti non rimosse passano molto tempo a creare la traccia dello stack. Sostituisci fillInStackTrace per vedere come questo può influenzare le prestazioni.


La mia risposta, purtroppo, è troppo lunga per postare qui. Quindi permettimi di sintetizzare qui e rimandarti a http://www.fuwjax.com/how-slow-are-java-exceptions/ per i dettagli grintosi.

La vera domanda qui non è "Quanto lenti sono i fallimenti riportati come eccezioni" rispetto al "codice che non fallisce mai"? " come la risposta accettata potrebbe farti credere. Invece, la domanda dovrebbe essere "Quanto lenti sono i fallimenti riportati come eccezioni" rispetto ai guasti segnalati in altri modi? " In generale, gli altri due modi di segnalare i fallimenti sono sia con valori sentinella che con wrapper di risultati.

I valori sentinella sono un tentativo di restituire una classe in caso di successo e un'altra in caso di fallimento. Puoi pensare a come restituire un'eccezione invece di lanciarne una. Ciò richiede una classe genitore condivisa con l'oggetto success e quindi un controllo "instanceof" e un cast di coppia per ottenere le informazioni su successo o fallimento.

Si scopre che a rischio di sicurezza del tipo, i valori di Sentinel sono più veloci delle eccezioni, ma solo di un fattore di circa 2x. Ora, questo può sembrare molto, ma questo 2x copre solo il costo della differenza di implementazione. In pratica, il fattore è molto più basso poiché i nostri metodi che potrebbero fallire sono molto più interessanti di alcuni operatori aritmetici come nel codice di esempio in altre parti di questa pagina.

I wrapper di risultati, d'altra parte, non sacrificano affatto la sicurezza del tipo. Comprendono le informazioni su successo e insuccesso in una singola classe. Quindi, invece di "instanceof" forniscono un "isSuccess ()" e getter per gli oggetti successo e fallimento. Tuttavia, gli oggetti risultato sono circa 2 volte più lenti rispetto all'utilizzo di eccezioni. Si scopre che la creazione di un nuovo oggetto wrapper ogni volta è molto più costosa rispetto a volte a volte generare un'eccezione.

Oltre a ciò, le eccezioni sono la lingua fornita per indicare che un metodo potrebbe fallire. Non c'è altro modo di dire solo dall'API su quali metodi ci si aspetta che siano sempre (per lo più) funzionanti e che ci si aspetta che segnalino un errore.

Le eccezioni sono più sicure delle sentinelle, più veloci degli oggetti risultato e meno sorprendenti di entrambe. Non sto suggerendo che try / catch sostituisca if / else, ma le eccezioni sono il modo giusto per segnalare un errore, anche nella logica di business.

Detto questo, vorrei sottolineare che i due modi più frequenti di incidere in modo sostanziale sulle prestazioni che ho incontrato sono la creazione di oggetti non necessari e loop nidificati. Se è possibile scegliere tra creare un'eccezione o non creare un'eccezione, non creare l'eccezione. Se è possibile scegliere tra creare un'eccezione a volte o creare un altro oggetto per tutto il tempo, quindi creare l'eccezione.


Non so se questi argomenti si riferiscono, ma una volta volevo implementare un trucco basandosi sulla traccia dello stack del thread corrente: volevo scoprire il nome del metodo, che ha attivato l'istanza all'interno della classe istanziata (yeap, l'idea è pazzesca, Ho completamente rinunciato). Così ho scoperto che chiamare Thread.currentThread().getStackTrace() è estremamente lento (a causa del metodo nativo dumpThreads che usa internamente).

Quindi Java Throwable , in modo corrispondente, ha un metodo nativo fillInStackTrace . Penso che il blocco killer- catch descritto in precedenza possa in qualche modo innescare l'esecuzione di questo metodo.

Ma lascia che ti racconti un'altra storia ...

In Scala alcune funzionalità sono compilate in JVM usando ControlThrowable , che estende Throwable e sovrascrive fillInStackTrace in un modo seguente:

override def fillInStackTrace(): Throwable = this

Quindi ho adattato il test sopra (la quantità di cicli è diminuita di dieci, la mia macchina è un po 'più lenta :):

class ControlException extends ControlThrowable

class T {
  var value = 0

  def reset = {
    value = 0
  }

  def method1(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0xfffffff) == 1000000000) {
      println("You'll never see this!")
    }
  }

  def method2(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0xfffffff) == 1000000000) {
      throw new Exception()
    }
  }

  def method3(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0x1) == 1) {
      throw new Exception()
    }
  }

  def method4(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0x1) == 1) {
      throw new ControlException()
    }
  }
}

class Main {
  var l = System.currentTimeMillis
  val t = new T
  for (i <- 1 to 10000000)
    t.method1(i)
  l = System.currentTimeMillis - l
  println("method1 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method2(i)
  } catch {
    case _ => println("You'll never see this")
  }
  l = System.currentTimeMillis - l
  println("method2 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method4(i)
  } catch {
    case _ => // do nothing
  }
  l = System.currentTimeMillis - l
  println("method4 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method3(i)
  } catch {
    case _ => // do nothing
  }
  l = System.currentTimeMillis - l
  println("method3 took " + l + " ms, result was " + t.value)

}

Quindi, i risultati sono:

method1 took 146 ms, result was 2
method2 took 159 ms, result was 2
method4 took 1551 ms, result was 2
method3 took 42492 ms, result was 2

Vedete, l'unica differenza tra method3 e method4 è che lanciano diversi tipi di eccezioni. Yeap, method4 è ancora più lento di method2 e method2 , ma la differenza è molto più accettabile.


Penso che il primo articolo si riferisca all'atto di attraversare lo stack di chiamate e creare una traccia di stack come la parte costosa, e mentre il secondo articolo non lo dice, penso che sia la parte più costosa della creazione di oggetti. John Rose ha un articolo in cui descrive diverse tecniche per accelerare le eccezioni . (Preassegnazione e riutilizzo di un'eccezione, eccezioni senza tracce di stack, ecc.)

Ma ancora - penso che questo dovrebbe essere considerato solo un male necessario, l'ultima risorsa. La ragione di John per farlo è quella di emulare funzionalità in altre lingue che non sono (ancora) disponibili nella JVM. NON si deve prendere l'abitudine di usare le eccezioni per il controllo del flusso. Soprattutto non per motivi di prestazioni! Come menzioni tu stesso al punto # 2, rischi di mascherare bug gravi nel tuo codice in questo modo, e sarà più difficile da mantenere per i nuovi programmatori.

I microbenchmarks in Java sono sorprendentemente difficili da ottenere (mi è stato detto), specialmente quando si entra nel territorio del JIT, quindi dubito fortemente che l'utilizzo delle eccezioni sia più veloce del "ritorno" nella vita reale. Ad esempio, sospetto che tu abbia tra 2 e 5 stack frame nel tuo test? Ora immagina che il tuo codice sarà invocato da un componente JSF distribuito da JBoss. Ora potresti avere una traccia dello stack lunga diverse pagine.

Forse potresti pubblicare il tuo codice di prova?


Basta confrontare diciamo Integer.parseInt al seguente metodo, che restituisce un valore predefinito nel caso di dati non analizzabili invece di lanciare un'eccezione:

  public static int parseUnsignedInt(String s, int defaultValue) {
    final int strLength = s.length();
    if (strLength == 0)
      return defaultValue;
    int value = 0;
    for (int i=strLength-1; i>=0; i--) {
      int c = s.charAt(i);
      if (c > 47 && c < 58) {
        c -= 48;
        for (int j=strLength-i; j!=1; j--)
          c *= 10;
        value += c;
      } else {
        return defaultValue;
      }
    }
    return value < 0 ? /* übergebener wert > Integer.MAX_VALUE? */ defaultValue : value;
  }

Fintanto che si applicano entrambi i metodi a dati "validi", entrambi funzioneranno all'incirca alla stessa velocità (anche se Integer.parseInt riesce a gestire dati più complessi). Ma non appena si tenta di analizzare dati non validi (ad es. Per analizzare "abc" 1.000.000 di volte), la differenza di prestazioni dovrebbe essere essenziale.


La mia opinione sulla velocità di eccezione rispetto al controllo dei dati a livello di codice.

Molte classi avevano un convertitore da stringa a valore (scanner / parser), anche rispettate e ben note librerie;)

di solito ha forma

class Example {
public static Example Parse(String input) throws AnyRuntimeParsigException
...
}

il nome dell'eccezione è solo un esempio, di solito è deselezionato (runtime), quindi la dichiarazione dei tiri è solo la mia immagine

qualche volta esiste una seconda forma:

public static Example Parse(String input, Example defaultValue)

mai lanciando

Quando il secondo non è disponibile (o il programmatore legge troppo meno documenti e usa solo prima), scrivi tale codice con un'espressione regolare. L'espressione regolare è fredda, politicamente corretta ecc .:

Xxxxx.regex(".....pattern", src);
if(ImTotallySure)
{
  Example v = Example.Parse(src);
}

con questo codice i programmatori non hanno costi di eccezioni. Ma ha un costo molto elevato paragonabile delle espressioni regolari SEMPRE a fronte di piccoli costi di eccezione a volte.

Uso quasi sempre in tale contesto

try { parse } catch(ParsingException ) // concrete exception from javadoc
{
}

senza analizzare lo stacktrace, ecc., credo che dopo le lezioni del tuo piuttosto veloce.

Non temere le eccezioni


Un ottimo post sulle prestazioni delle eccezioni è:

https://shipilev.net/blog/2014/exceptional-performance/

Istanziazione vs riutilizzo esistente, con traccia di stack e senza, ecc:

Benchmark                            Mode   Samples         Mean   Mean error  Units

dynamicException                     avgt        25     1901.196       14.572  ns/op
dynamicException_NoStack             avgt        25       67.029        0.212  ns/op
dynamicException_NoStack_UsedData    avgt        25       68.952        0.441  ns/op
dynamicException_NoStack_UsedStack   avgt        25      137.329        1.039  ns/op
dynamicException_UsedData            avgt        25     1900.770        9.359  ns/op
dynamicException_UsedStack           avgt        25    20033.658      118.600  ns/op

plain                                avgt        25        1.259        0.002  ns/op
staticException                      avgt        25        1.510        0.001  ns/op
staticException_NoStack              avgt        25        1.514        0.003  ns/op
staticException_NoStack_UsedData     avgt        25        4.185        0.015  ns/op
staticException_NoStack_UsedStack    avgt        25       19.110        0.051  ns/op
staticException_UsedData             avgt        25        4.159        0.007  ns/op
staticException_UsedStack            avgt        25       25.144        0.186  ns/op

A seconda della profondità della traccia dello stack:

Benchmark        Mode   Samples         Mean   Mean error  Units

exception_0000   avgt        25     1959.068       30.783  ns/op
exception_0001   avgt        25     1945.958       12.104  ns/op
exception_0002   avgt        25     2063.575       47.708  ns/op
exception_0004   avgt        25     2211.882       29.417  ns/op
exception_0008   avgt        25     2472.729       57.336  ns/op
exception_0016   avgt        25     2950.847       29.863  ns/op
exception_0032   avgt        25     4416.548       50.340  ns/op
exception_0064   avgt        25     6845.140       40.114  ns/op
exception_0128   avgt        25    11774.758       54.299  ns/op
exception_0256   avgt        25    21617.526      101.379  ns/op
exception_0512   avgt        25    42780.434      144.594  ns/op
exception_1024   avgt        25    82839.358      291.434  ns/op

Per altri dettagli (incluso l'assemblatore x64 di JIT) leggi il post originale del blog.

Ciò significa che Hibernate / Spring / etc-EE-shit sono lenti a causa delle eccezioni (xD) e riscrivono il flusso di controllo dell'app lontano dalle eccezioni (sostituirlo con continure/ breake restituire i booleanflag come in C dalla chiamata al metodo) migliorare le prestazioni dell'applicazione 10x-100x , a seconda di quanto spesso li si getta))


Ho modificato la risposta di @Mecki in precedenza per fare in modo che method1 restituisca un valore booleano e un controllo nel metodo di chiamata, in quanto non si può semplicemente sostituire un'eccezione senza nulla. Dopo due esecuzioni, method1 era ancora il più veloce o veloce come method2.

Ecco l'istantanea del codice:

// Calculates without exception
public boolean method1(int i) {
    value = ((value + i) / i) << 1;
    // Will never be true
    return ((i & 0xFFFFFFF) == 1000000000);

}
....
   for (i = 1; i < 100000000; i++) {
            if (t.method1(i)) {
                System.out.println("Will never be true!");
            }
    }

e risultati:

Esegui 1

method1 took 841 ms, result was 2
method2 took 841 ms, result was 2
method3 took 85058 ms, result was 2

Esegui 2

method1 took 821 ms, result was 2
method2 took 838 ms, result was 2
method3 took 85929 ms, result was 2




exception-handling