2009-10-05 9 views
10

InputStreams e OutputStreams in Java close() in caso di distruzione? Capisco perfettamente che questa possa essere una cattiva forma (specialmente nel mondo C e C++), ma sono curioso.Input/OutputStreams si chiude in caso di distruzione?

Inoltre, supponiamo di avere il seguente codice:

private void foo() 
{ 
    final string file = "bar.txt"; 
    Properties p = new Properties(); 
    p.load(new FileInputStream(file)); 
    //... 
} 

Fa il FileInputStream senza nome esce dall'ambito dopo p.load(), e quindi vengono distrutte, un po 'come C++ regole di visibilità? Ho provato a cercare l'ambito delle variabili anonimo per Java su Google, ma non è risultato quello che pensavo sarebbe stato.

Grazie.

+0

Grazie a tutti per l'aiuto! – Calyth

+0

Si noti che il 'try' con risorse in Java 7 o successive renderebbe la risoluzione di questo problema un'operazione relativamente semplice (richiede solo un'assegnazione variabile aggiuntiva e, naturalmente,' try' stesso). Si noti inoltre che il codice precedente genererebbe un avvertimento (sulla chiusura mancante) nel mio ambiente Eclipse. –

risposta

16

Prima risposta: non esiste una cosa come "distruzione" (nel senso C++) in Java. C'è solo il Garbage Collector, che può o non può svegliarsi e fare il suo lavoro quando vede un oggetto che è pronto per essere raccolto. GC in Java è generalmente inaffidabile.

Seconda risposta: a volte sì, a volte no, ma non vale la pena rischiare. Da Elliote Rusty Harold's Java IO:

Non tutti i flussi devono essere chiusi — flussi di uscita di byte non devono essere chiusi, per esempio. Tuttavia, i flussi associati ai file e le connessioni di rete devono essere sempre chiusi quando hai finito con loro. Ad esempio, se si apre un file per la scrittura e si trascura di chiuderlo quando lo si esegue tramite , altri processi potrebbero essere bloccati dalla lettura o in scrittura su quel file.

Secondo Harold, lo stesso vale per i flussi di input o di output. Ci sono alcune eccezioni (egli dice System.in), ma in generale, si sta correndo un rischio se non si chiudono i flussi di file quando hai finito. E chiudili in un blocco finale, per assicurarti che vengano chiusi anche se viene lanciata un'eccezione.

5

No, non ci sono distruttori in Java. Potrebbero esserci altri riferimenti all'oggetto, anche dopo che un particolare riferimento ad esso va fuori ambito (o è modificato). Se l'oggetto non è più raggiungibile, il flusso potrebbe avere il suo finalizzatore chiamato qualche tempo dopo che chiuderà il flusso.

Properties.load è peculiare nella chiusura del flusso passato ad esso. Modifica: Properties.loadFromXML è il metodo speciale che sembra aver pensato circa cinque anni fa. (Documento API dovrebbe probabilmente dire prima piuttosto che dopo.) Grazie @tzimnoch.

+0

Non ci sono distruttori, ma ci sono i finalizzatori. E i finalizzatori per la maggior parte delle classi InputStream/OuputStream di java.io DO chiudono lo stream. – ChssPly76

+5

@ ChssPly76: Giusto, ma non vi è alcuna garanzia * di sorta su quando, o anche se, i finalizzatori verranno richiamati. Nessuna. –

+1

https://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#load(java.io.InputStream) "Il flusso specificato rimane aperto dopo il ritorno di questo metodo." – tzimnoch

3

La variabile esce dal campo di applicazione e pertanto viene distrutta. Ma in Java, c'è una grande differenza tra una variabile e l'oggetto che la variabile punta a.

L'oggetto puntato non viene distrutta solo quando la variabile passa nell'ambito. L'oggetto viene distrutto solo quando il motore di runtime Java decide di andare in giro a distruggere oggetti non puntati da alcuna variabile nell'ambito.

6

Avevo l'abitudine di presumere che gli stream sarebbero stati chiusi automaticamente tramite la garbage collection, ma prove aneddotiche indicano che il mancato chiuderle manualmente comporta una perdita di risorse. Si vuole fare qualcosa di simile, invece:

InputStream stream = null; 

try { 
    stream = new FileInputStream("bar.txt"); 
    Properties p = new Properties(); 
    p.load(stream); 
} 
catch(Exception e) { 
    // error handling 
} 
finally { 
    closeQuietly(stream); 
} 

closeQuietly() è un metodo su IOUtils nella libreria commons-io di Apache.

3

La risposta breve è "forse, ma non scommetterci!".

Da qualche parte nella pila di classi che implementano FileInputStream è una classe che ha un finalizer che chiude effettivamente il flusso (e rilascia la risorsa) quando viene eseguito.

Il problema è che non è possibile garantire che il finalizzatore venga eseguito. Citando dalla JLS (sezione 12.6):

Il linguaggio di programmazione Java non specificare quanto tempo un finalizzatore sarà invocato, se non per dire che sarà accadere prima che la memoria per l'oggetto viene riutilizzato.

Questo rende flusso di finalizzazione problematico:

  1. Se il vostro oggetto Stream non diventa mai spazzatura, non potrà mai essere finalizzato.
  2. Se l'oggetto Stream è di proprietà, potrebbe richiedere molto tempo prima che venga raccolto e finalizzato.
  3. La JVM potrebbe dover eseguire un ciclo GC aggiuntivo dopo che l'oggetto è stato identificato come non raggiungibile prima dell'esecuzione del finalizzatore. Questo è certamente consentito dal JLS!
  4. È tecnicamente legale per una JVM non eseguire mai e poi mai i finalizzatori, a condizione che non riutilizzi mai la memoria degli oggetti con i finalizzatori. (Non sono a conoscenza di JVM di qualità di produzione che prendono questa linea, ma non si sa mai ...)
+0

Penso che se la JVM avesse risorse limitate finirebbe con gli oggetti file chiusi. (Non per perdonare di non ripulire dopo di te.) –

+0

Si potrebbe pensare che ... ma AFAIK, l'esaurimento dei descrittori di file non innesca un'esecuzione GC che rileverà che altri oggetti di flusso sono (ipoteticamente) ora definibili. –

+0

@mike jones: no, non è il caso. Questo può essere praticamente provato. L'esaurimento della memoria può innescare la garbage collection (è persino richiesto di fare del suo meglio prima di lanciare un 'OutOfMemoryError'), ma gc * non * è identico alla finalizzazione. La garbage collection farà sì che gli oggetti con i finalizzatori siano * enqueued * per la finalizzazione successiva, ma è completamente indefinito quando avverrà la finalizzazione.Quindi, mentre l'esecuzione dei descrittori di file (o di qualsiasi altra risorsa non di memoria) non attiva comunque gc (come già sottolineato da Stephen), anche se gc viene attivato, non impone la finalizzazione. – Holger

Problemi correlati