2010-02-02 14 views
14

Ci sono dei costi di prestazioni creando, lanciare e catturare eccezioni in Java?Il costo delle prestazioni della codifica "sviluppo basato su eccezioni" in Java?

Sto pianificando di aggiungere "sviluppo guidato da eccezioni" in un progetto più ampio. Vorrei progettare le mie eccezioni e includerle nei miei metodi, costringendo gli sviluppatori a prendere e fare il lavoro appropriato.

Ad esempio, se si dispone di un metodo per ottenere un utente dal database in base a un nome.

public User getUser(String name); 

Tuttavia, è possibile che l'utente potrebbe essere nullo ed è comune a dimenticare di controllare questo prima di utilizzare il metodo pubblico di un utente.

User user = getUser("adam"); 

int age = user.getAge(); 

che comporterebbe NullPointerException e un arresto anomalo. Tuttavia, se ho fatto un controllo, prima di tornare l'utente-oggetto, se nullo e genera un 'UserIsNullException':

public User getUser(String name) throws UserIsNullException; 

forzo implementor di pensare e agire:

try { 

    User user = getUser("adam"); 

    int age = user.getAge(); 

}catch(UserIsNullException e) { 

} 

Si fa il codice è molto più sicuro per arresti anomali e elimina più bug. Diciamo che il sito ha centinaia di visitatori all'ora e questo modello di design è usato praticamente ovunque.

In che modo un simile approccio al design influisce sulle prestazioni? I benefici fuori-pesano il costo o è semplicemente una cattiva programmazione?

Grazie per qualsiasi aiuto!

AGGIORNAMENTO! Per essere chiari, la mia attenzione non è quella di avvolgere una NullPointerException, come potrebbe suggerire il mio esempio. L'obiettivo è quello di forzare il realizzatore di scrivere un try/catch, salvare il mal di testa di un incidente vero e proprio dal momento che una:

utente == null

è stato dimenticato. La questione riguarda confronto questi due modelli di progettazione:

int age; 

try { 

User user = getUser("adam"); 

age = user.getAge(); 

}catch(UserIsNullException e) { 

age = 0; 

} 

rispetto:

int age; 

User user = getUser("adam"); 

if(user != null) { 
age = user.getAge(); 
} else { 
age = 0; 
} 
+6

Gli oggetti nulli sono qualcosa che può esserti d'aiuto. Stavo leggendo il libro "Refactoring" di Martin Fowler l'altro giorno e descrive l'uso di un oggetto specifico che agisce nel luogo in cui si restituisce un null, evitando di dover controllare NULL per tutto il tempo. Avevo una rapida occhiata nel libro, è a pagina 260 "Introduci oggetto nullo", se ne hai una copia. Non proverò a spiegarlo qui come è descritto molto bene nel libro. – Tony

+0

@Tony Penso che il tuo commento meriti di diventare una risposta completa :-) – KLE

+0

@KLE: Grazie, ho pensato di metterlo come commento in quanto non è propriamente una risposta alla sua domanda, ma lo aggiungerò come rispondi anche tu – Tony

risposta

3

Costruire un quantità della traccia dello stack a circa un migliaio di istruzioni di base. Ha un costo definito.


Anche se non chiedere, molte persone probabilmente vi dirà l'approccio immaginate non sembra molto attraente ... :-(

Il codice si stanno costringendo sui vostri chiamanti è davvero brutto, difficile da scrivere e da mantenere.

per essere più precisi, e cercare di ottenere capito, cercherò di evidenziare alcuni punti.

  • L'altro sviluppatore è respons possibilità di verificare la nullità dell'Utente ricevuto dalla propria API (se la documentazione lo indica chiaramente). Il suo codice esegue tali verifiche internamente regolarmente, quindi può farlo anche dopo una chiamata alla tua API.Ancor di più, questo darà al suo codice una certa omogeneità.

  • Quando riutilizzare un'eccezione esistente è appropriato, è molto meglio che crearne di propri. Ad esempio, se è stato un errore chiamare la tua API e chiedere un utente inesistente, puoi lanciare un IllegalArgumentException.

+1

Un'eccezione generata costa qualcosa da gestire, ma un'eccezione non generata è gratuita. Le eccezioni a costo zero sono una funzionalità di JVM. – Will

+0

@Will Vuoi dire, libero quando costruisco un'istanza di eccezione ma non la lancio? Come in 'new Exception();'? Hai una fonte ufficiale per queste informazioni? – KLE

+0

Il normale percorso attraverso il codice non deve fare eccezione. Eccezioni di costo zero significano che questo percorso non viene rallentato perché il codice * potrebbe * in altre circostanze generare un'eccezione. Nello sviluppo guidato da eccezioni, le eccezioni sono ancora eccezionali. Pertanto, non vi è generalmente alcun impatto notevole sul rendimento derivante dall'uso dell'EDD. – Will

8

"che si tradurrebbe in NullPointerException e un incidente."

a NullPointerException è un'eccezione e può essere catturato in una presa.

Quindi l'eccezione aggiuntiva è ridondante a meno che non aggiunga chiarezza al codice. In questo caso, in realtà non lo è. In effetti, hai appena preso un'eccezione di errore del programmatore non controllata e promosso a in un'eccezione controllata.

Creare un'eccezione controllata per l'errore di un programmatore significa in realtà che il codice deve gestire esplicitamente la possibilità che il programmatore abbia introdotto un errore, quando non lo ha fatto.

+1

Aggiunge chiarezza poiché costringe l'implementatore a scrivere un tentativo/cattura. – corgrath

+9

Rimuove la chiarezza perché il programmatore, costretto a mettere i try-catch per nuove eccezioni ovunque, avrà un sacco di codice quando nell'altro caso avrebbero solo il codice per risolvere il problema, non il controllo del codice che il programmatore non aveva incasinato su. – Will

+1

@corgrath: ai programmatori non piace essere costretti a fare nulla, specialmente qualcosa con un costo di manutenibilità così alto (devi cambiare il codice esistente ovunque) e guadagni relativamente poco. Se lo fai alla tua squadra, ti lanceranno. –

1

Vedere this answer sulle prestazioni con eccezioni.

Fondamentalmente nella propria idea si sta eseguendo il wrapping di una RuntimeException, NullPointerException, in un'eccezione controllata; imho Direi che il problema può essere gestito a livello aziendale con una ObjectNotFoundException: non hai trovato l'utente, il fatto che l'utente sia nullo e che questo generi errori viene dopo.

1

Mi piace questo stile di codifica in quanto rende molto chiaro ciò che sta accadendo dal punto di vista di qualcuno che utilizza la tua API. Talvolta mi chiamo anche i metodi API getMandatoryUser(String) e getUserOrNull(String) per distinguere tra metodi che non restituiranno mai null e quelli che possono restituire null.

Per quanto riguarda le prestazioni, a meno che non si stia scrivendo un pezzo di codice molto lento per la latenza, il sovraccarico delle prestazioni sarà trascurabile. Tuttavia, vale la pena ricordare che ci sono alcune buone pratiche quando si tratta di provare/catturare blocchi. Ad esempio, credo che Effective Java sostenga la creazione di un blocco try/catch al di fuori di un ciclo per evitare di crearlo a ogni iterazione; per esempio.

boolean done = false; 

// Initialise loop counter here to avoid resetting it to 0 if an Exception is thrown. 
int i=0;  

// Outer loop: We only iterate here if an exception is thrown from the inner loop. 
do { 
    // Set up try-catch block. Only done once unless an exception is thrown.  
    try { 
    // Inner loop: Does the actual work. 
    for (; i<1000000; ++i) { 
     // Exception potentially thrown here. 
    } 

    done = true; 
    } catch(Exception ex) { 
    ... 
    } 
} while (!done); 
6

C'è un costo di prestazioni ma questo non è il problema principale. Vuoi veramente che il tuo codice sia pieno di blocchi di cattura come il tuo esempio? È orribile.

In genere un'applicazione Web ha un gestore di eccezioni principale in cui ogni cosa non viene rilevata, almeno in questo modo quando si verifica un errore durante l'elaborazione. Con tutto ciò che accelera eccezionalmente durante il flusso del processo, sarà come scendere un paio di rampe di scale.

Alcune eccezioni sono casi molto speciali che ci si aspetta e in grado di gestire. Tuttavia, in generale, le eccezioni appaiono quando qualcosa di inaspettato o incontrollabile è andato storto. Da quel punto, è probabile che gli oggetti si trovino in uno stato negativo, poiché i passaggi successivi si aspettano che le cose si trovino lì che non si sono verificate a causa dell'eccezione, e il tentativo di procedere attiverà solo più eccezioni. È molto meglio lasciare eccezioni inaspettate, in modo che possano essere catturate da un gestore centrale.

+1

+1 per l'analogia! – James

1

Creazione di un'eccezione, generazione di un'eccezione (con una traccia di stack), rilevamento di un'eccezione e quindi raccolta di dati inutili tale eccezione (eventualmente) è molto più lenta rispetto a una semplice verifica.

In definitiva, si può probabilmente discuterne con lo stile, ma penso che sia uno stile pessimo.

1

Gli oggetti nulli sono qualcosa che può esserti d'aiuto. Stavo leggendo il libro "Refactoring" di Martin Fowler l'altro giorno e descrive l'uso di un oggetto specifico che agisce nel luogo in cui si restituisce un null, evitando di dover controllare NULL per tutto il tempo.

Non cercherò di spiegarlo qui come descritto molto bene nel libro.

+0

@Tony e @Oscar Reyes I NullObjects sono interessanti, tuttavia è necessario interrompere se l'utente è nullo, poiché di solito faccio molto di più con l'oggetto utente. Se dovessi utilizzare NullObjects, come NullUser, ho bisogno di usare "user instanceof NullUser" per capire se l'Utente esiste con quel nome. – corgrath

+1

Se è necessario interrompere quando è nullo, perché non lasciarlo così com'è e lasciare che NullPointerException scorra fino a un blocco catch che interrompe il processo (quando l'utente è nullo o quando qualsiasi altro oggetto richiesto è null ... non c'è bisogno di popolare con più eccezioni quando ce n'è uno che in realtà significa ciò che si desidera –

+0

Lo capisco, ma diciamo che voglio ottenere Utente e un Veicolo, entrambi potrebbero lanciare NullPointerException - come faresti a sapere quale è andato male? Non è quindi meglio lanciare una UserIsNullException e una VehicleIsNullException? In tal caso, è possibile effettuare la corretta gestione degli errori. – corgrath

5

Personalmente, penso che sia un'idea brutta e antipatica.

Il solito modo per incoraggiare le persone a controllare i valori nulli è quello di utilizzare le annotazioni come @Nullable (e il suo opposto, @NotNull, per le funzioni che sono garantiti per tornare non nulli). Impostando annotazioni simili sui parametri (in modo che le aspettative dei parametri siano impostate), gli IDE di qualità e i controllori di bug (come i FindBug) possono quindi generare tutti gli avvertimenti necessari quando il codice non esegue un controllo sufficiente.

Queste annotazioni sono disponibili in JSR-305 e apparentemente c'è anche un reference implementation.


Per quanto riguarda le prestazioni va, creando l'eccezione è la parte costosa (ho letto che è a causa della stacktrace riempimento, tra le altre cose). Lanciare l'eccezione è economico, ed è un control-transfer technique utilizzato in JRuby.

+2

Scala utilizza anche questo per simulare la parola chiave 'break' da Java (vedere Breaks.scala e NoStackTrace. scala qui: http://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/library/scala/util/control) –

+0

Se creare un'eccezione è costoso, ma il lancio è economico, non ciò significa che il lancio di un'eccezione diventa automaticamente costoso? Dal momento che devi effettivamente creare un'eccezione da buttare. – corgrath

+1

@corgrath: se vuoi lanciare una vera eccezione, allora sì, il costo è lì. Se si utilizzano le eccezioni come meccanismo di controllo del trasferimento, si crea un oggetto di eccezione e lo si memorizza, quindi si gira lo stesso oggetto. Dal momento che è appena usato per il controllo-trasferimento, non ti interessa la precisione delle informazioni di backtrace, quindi questo funziona. –

5

In generale, le eccezioni sono estremamente costose in Java e devono essere non da utilizzare per il controllo del flusso!

Il punto di eccezione è per descrivere qualcosa eccezionali, per esempio NumberIsZeroException non è poi così eccezionale, mentre PlanesWingsJustDetachedException è chiaramente qualcosa di veramente eccezionale. Se è davvero eccezionale nel tuo software che l'utente sia null perché c'è il danneggiamento dei dati o il numero ID non corrisponde a nulla o qualcosa del genere, allora è giusto usare un'eccezione per questo.

Quali eccezioni causano anche divergenze dal "percorso felice". Anche se questo non si applica al tuo codice di esempio, a volte è molto vantaggioso utilizzare Null Object invece di restituire plain null.

+0

Quando Java era giovane, ho cercato di ignorare i limiti dell'array controllando che ArrayIndexOutOfBoundsException potesse farlo per me. Il codice era approssimativamente 30 volte più veloce quando aggiungevo la semplice logica 'if (x <0)', anche con otto istruzioni if. Anche l'uso della memoria era molto più basso. Ultimamente non ho eseguito test delle prestazioni, ma temo che in questi giorni si verificheranno rallentamenti simili. –

0

In che modo un tale progetto si avvicina alle prestazioni dell'effetto ? I vantaggi superano il costo o è semplicemente la codifica errata ?

Avevo detto che non c'è quasi alcun vantaggio di questo stile di programmazione. Eccezione deve essere intesa come (beh ...) eccezione. Dovrebbe solo accadere in una situazione non normale. Come definisci la "situazione normale" è praticamente discutibile ...

6

Esiste una penalità delle prestazioni per generare un'eccezione, ma di solito è accettabile poiché il codice di gestione delle eccezioni viene eseguito solo nei casi eccezionali. Se inizi a utilizzare le eccezioni per il controllo del flusso, stai cambiando il solito caso e rendendoli attesi. Consiglio vivamente di non farlo.

0

È possibile dimenticare le eccezioni ed evitare problemi di prestazioni. Questo rende la tua API parlare da sola.

int getUserAge(String userName) 
{ 
    int age = 0; 
    UserSearchResult result = getUser(userName); 
    if (result.getFoundUser()) 
    { 
     User user = result.getUser(); 
     age = user.getAge(); 
    } 
    return age; 
} 
0

Come per l'ultima modifica. Penso che (come suggeriva Tony) l'uso del pattern NullObject sarebbe più utile qui.

considerare questo terzo scenario:

class NullUser extends User { 

    public static NullUser nullUser = new NullUser(); 

    private NullUser(){} 

    public int getAge() { 
     return 0; 
    } 
} 

//Later... 
int age; 

User user = getUser("adam"); // return nullUser if not found. 
age = user.getAge();   // no catch, no if/null check, pure polymorphism 
0

eccezioni costano molto, perché ogni volta che viene generata un'eccezione, la traccia dello stack deve essere creato e popolato.

Immaginate un'operazione di trasferimento di equilibrio che non riesce nell'1% dei casi a causa della mancanza di fondi. Anche con questa percentuale relativamente bassa di guasti, le prestazioni potrebbero essere gravemente compromesse. Vedere here per il codice sorgente e i risultati del benchmark.

Problemi correlati