2009-11-02 23 views
8

Sto lavorando a un'enorme applicazione Java legacy, con un sacco di cose scritte a mano, che al giorno d'oggi si lascia gestire da un framework.Troppi handle di file aperti

Il problema che sto affrontando in questo momento è che stiamo esaurendo gli handle di file sul nostro server Solaris. Mi piacerebbe sapere qual è il modo migliore per tenere traccia degli handle di file aperti? Dove guardare e cosa può causare l'esaurimento degli handle di file aperti?

Non riesco a eseguire il debug dell'applicazione in Solaris, solo sul mio ambiente di sviluppo Windows. È anche ragionevole analizzare gli handle di file aperti sotto Windows?

+1

con lsof -p PID, la voce più comune è il seguente: java 19157 dev 131u unix 105,98572 0t829 55.050.244/devices/pseudo/tl @ 0: ticots -> (socketpair: 0x1810c) (0x300199eed50) Qualche idea di cosa significa e come posso combatterlo? – dlinsin

risposta

8

Una cosa buona che ho trovato per rintracciare il file non chiusa gestisce è FindBugs:

http://findbugs.sourceforge.net/

Esso controlla molte cose, ma una delle più utile è risorsa aperta/operazioni stretti. È un programma di analisi statico eseguito sul codice sorgente ed è anche disponibile come plug-in di eclissi.

+0

Come testimonial personale, ho riscontrato un problema simile a quello che l'OP aveva (la mia app stava generando eccezioni perché non poteva aprire più file perché avevo troppi descrittori di file aperti). L'esecuzione del codice tramite findbugs ha aiutato a identificare tutti i punti in cui i file non erano chiusi. Problema risolto! – tth

+0

Sì, una volta mi ha aiutato a trovare un'intera serie di luoghi in cui close() non era stato chiamato in un blocco finale appropriato. – Benj

+0

anche se non ha risolto il mio problema direttamente, è stato un grande suggerimento! – dlinsin

0

Potrebbe sicuramente darvi un'idea. Poiché è Java, i meccanismi di apertura/chiusura dei file dovrebbero essere implementati in modo simile (a meno che una delle JVM non sia implementata in modo errato). Vorrei raccomandare l'uso di File Monitor su Windows.

1

Vorrei iniziare chiedendo al mio sysadmin di ottenere un elenco di tutti i descrittori di file aperti per il processo. Sistemi diversi lo fanno in diversi modi: Linux, ad esempio, ha la directory /proc/PID/fd. Ricordo che Solaris ha un comando (forse pfiles?) Che farà la stessa cosa - il tuo sysadmin dovrebbe saperlo.

Tuttavia, a meno che non vengano visualizzati molti riferimenti allo stesso file, una lista non può aiutarti. Se si tratta di un processo del server, probabilmente ha un sacco di file (e socket) aperti per un motivo. L'unico modo per risolvere il problema è regolare il limite di sistema sui file aperti: è inoltre possibile controllare il limite per utente con ulimit, ma nella maggior parte delle installazioni correnti è uguale al limite di sistema.

7

Su Windows si può guardare handle di file aperti utilizzando Process Explorer:

http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx

Su Solaris è possibile utilizzare "lsof" per monitorare il file aperto gestisce

+0

Grazie! Ho usato lsof, purtroppo ci sono un sacco di cose in corso e non so davvero come restringere i risultati di lsof a ciò che è rilevante e ciò che non è interessante. – dlinsin

+1

Il risultato da Windows non deve essere estrapolato ai sistemi * nix. Hanno diversi meccanismi di apertura dei file. –

1

Non è una risposta diretta alla tua domanda, ma questi problemi potrebbero essere il risultato del rilascio di risorse file in modo errato nel codice legacy. Con l'esempio, se si sta lavorando con le classi FileOutputsStream assicurarsi che i metodi di chiusura vengono chiamati in un blocco finally come in questo esempio:

FileOutputsStream out = null; 
try { 
    //You're file handling code 
} catch (IOException e) { 
    //Handle 
} finally { 
    if (out != null) { 
    try { out.close(): } catch (IOException e) { } 
    } 
} 
+0

Quello che ha detto. sembra che gli handle di file non vengano mai rilasciati. – ChadNC

+0

Grazie per il consiglio generale, ma ho cercato tutte le occorrenze di java.io. * e ho verificato che si trovassero in un blocco try-catch-finally. – dlinsin

2

Per rispondere alla seconda parte della domanda:

ciò che può causare handle di file aperti per esaurirsi?

Aprire molti file, ovviamente, e quindi non chiuderli.

Lo scenario più semplice è che i riferimenti a qualunque oggetto contenga le maniglie native (ad esempio, FileInputStream) vengono gettati via prima di essere chiusi, il che significa che i file rimangono aperti finché gli oggetti non sono finalizzati.

L'altra opzione è che gli oggetti sono memorizzati da qualche parte e non chiusi. Un heap dump potrebbe essere in grado di dirti cosa indugia dove (jmap e jhat sono inclusi nel JDK, oppure puoi usare jvisualvm se vuoi una GUI). Probabilmente sei interessato alla ricerca di oggetti che possiedono FileDescriptor s.

2

Questo piccolo script mi ​​aiuta a tenere d'occhio il conteggio dei file aperti quando ho bisogno di un conteggio dei test. Se è stato utilizzato su Linux, quindi per Solaris si dovrebbe patch (può essere :))

#!/bin/bash 
COUNTER=0 
HOW_MANY=0 
MAX=0 
# do not take care about COUNTER - just flag, shown should we continie or not 
while [ $COUNTER -lt 10 ]; do 
    #run until process with passed pid alive 
    if [ -r "/proc/$1" ]; then 
     # count, how many files we have 
     HOW_MANY=`/usr/sbin/lsof -p $1 | wc -l` 
     #output for live monitoring 
     echo `date +%H:%M:%S` $HOW_MANY 
     # uncomment, if you want to save statistics 
     #/usr/sbin/lsof -p $1 > ~/autocount/config_lsof_`echo $HOW_MANY`_`date +%H_%M_%S`.txt 

     # look for max value 
     if [ $MAX -lt $HOW_MANY ]; then 
      let MAX=$HOW_MANY 
      echo new max is $MAX 
     fi 
     # test every second. if you don`t need so frequenlty test - increase this value 
     sleep 1 
    else 
     echo max count is $MAX 
     echo Process was finished 
     let COUNTER=11 
    fi 
done 

Inoltre è possibile provare a giocare con JVM ontion -Xverify: nessuno - è necessario disattivare la verifica vaso (se la maggior parte delle i file aperti sono vasi ...). Per perdite attraverso FileOutputStream non chiuso è possibile utilizzare findbug (mentorato in precedenza) o provare a trovare l'articolo su come patch standard java FileOutputStream/FileInputStream, dove è possibile vedere, chi apre i file e ha dimenticato di chiuderli. Purtroppo, non riesco a trovare questo articolo in questo momento, ma questo esiste già :) Pensa anche all'aumento di filelimit - per i kernel nix * aggiornati non è un problema gestire più di 1024 fd.

2

Questo potrebbe non essere pratico nel tuo caso, ma quello che ho fatto una volta, quando ho avuto un problema simile con connessioni al database aperte era ignorare la funzione "aperto" con la mia. (Praticamente avevo già questa funzione perché avevamo scritto il nostro pool di connessioni.) Nella mia funzione ho quindi aggiunto una voce a una tabella che registrava l'open. Ho fatto una chiamata stack trace e ho salvato l'identificativo del chiamante, insieme al tempo chiamato e ho dimenticato cos'altro. Quando la connessione è stata rilasciata, ho cancellato la voce della tabella. Poi ho avuto una schermata in cui è possibile scaricare l'elenco delle voci aperte. È quindi possibile visualizzare il timestamp e vedere facilmente quali connessioni sono state aperte per un numero improbabile di tempo e quali funzioni sono state aperte.

Da questo siamo stati in grado di rintracciare rapidamente la coppia di funzioni che stavano aprendo i collegamenti e che non riescono a chiuderle.

Se hai un sacco di handle di file aperti, le probabilità sono che si sta riuscendo a chiuderle quando hai finito da qualche parte. Dici di aver controllato i blocchi try/finally giusti, ma sospetto che da qualche parte nel codice ti sia sfuggita una cosa sbagliata, o che tu abbia una funzione che le mani e non arrivi mai alla fine. Suppongo che sia anche possibile che tu stia davvero chiudendo correttamente ogni volta che apri un file, ma stai aprendo centinaia di file contemporaneamente. In questo caso, non sono sicuro di cosa si possa fare se non una seria riprogettazione del programma per manipolare meno file o una seria riprogettazione del programma per accodare i file di accesso. (A questo punto aggiungo il solito, "Senza conoscere i dettagli della tua applicazione, ecc.)

1

Vorrei controllare le impostazioni dell'ambiente sulla scatola di Solaris. Credo che per impostazione predefinita Solaris consenta solo 256 handle di file per processo Per un'applicazione server, specialmente se è in esecuzione su un server dedicato, questo è molto basso Figura 50 o più descrittori per aprire JRE e JAR di libreria, e quindi almeno un descrittore per ogni richiesta in entrata e query di database, probabilmente di più, e si può vedere come questo proprio non è tagliato la senape per un server serio.

Dai un'occhiata alla file /etc/system, per i valori di rlim_fd_cur e rlim_fd_max, per vedere cosa ha impostato il tuo sistema. Quindi considera se questo è ragionevole (puoi vedere quanti descrittori di file sono aperti mentre il server è in esecuzione con il comando lsof, idealmente con il parametro -p [ID processo]

2

Vale la pena tenere presente che le prese aperte consumano anche handle di file su sistemi Unix.Quindi potrebbe essere qualcosa di simile a una perdita del pool di connessione del database (ad esempio, le connessioni del database aperte non vengono chiuse e restituite al pool) che sta portando a questo problema - sicuramente ho visto questo errore prima causato da una perdita del pool di connessioni.

0

Google per un'applicazione chiamata filemon dagli interni di sistema.

BTW, per rintracciare questo potrebbe essere possibile utilizzare qualcosa come aspectj per registrare tutte le chiamate che aprono e chiudono i file e registrano dove si verificano.

+0

E questo è stato votato perché? – vickirk

0

Questo è uno schema di codifica che aiuta a trovare risorse non chiuse. Chiude le risorse e si lamenta anche nel registro del problema.

class 
{ 
    boolean closed = false; 
    File file; 

    close() { 
     closed = true; 
     file.close(); 
    } 

    finalize() { 
     if (!closed) { 
      log error "OI! YOU FORGOT TO CLOSE A FILE!" 
     file.close(); 
    } 
} 

Avvolgere la file.close sopra() chiama in blocchi try-catch che ignorano gli errori.

Inoltre, Java 7 ha una nuova funzione 'try-with-resource' che può chiudere automaticamente le risorse.

+0

Il suo design davvero pessimo da usare finalize() http://www.informit.com/articles/article.aspx?p=1216151&seqNum=7 –

Problemi correlati