2009-11-02 20 views
8

Estoy trabajando en una enorme aplicación de legado de Java, con una gran cantidad de cosas escritas a mano, que hoy en día permitiría manejar un marco.Demasiados manejadores de archivos abiertos

El problema que estoy enfrentando ahora es que nos estamos quedando sin controladores de archivos en nuestro servidor Solaris. Me gustaría saber cuál es la mejor manera de rastrear archivos abiertos maneja? ¿Dónde mirar y qué puede hacer que los archivos abiertos se agoten?

No puedo depurar la aplicación en Solaris, solo en mi entorno de desarrollo de Windows. ¿Es incluso razonable analizar los identificadores de archivos abiertos en Windows?

+1

con un lsof -p PID, la entrada más común es el siguiente: java 19157 dev 131u UNIX 105,98572 0t829 55050244/dispositivos/seudo/tl @ 0: ticots -> (socketpair: 0x1810c) (0x300199eed50) ¿Alguna idea de lo que esto significa y cómo puedo combatirlo? – dlinsin

Respuesta

8

Una buena cosa que he encontrado para la localización de archivo sin cerrar maneja es FindBugs:

http://findbugs.sourceforge.net/

Se comprueba muchas cosas, pero una de las más útiles es el recurso abierto/cerrar operaciones. Es un programa de análisis estático que se ejecuta en su código fuente y también está disponible como un complemento de eclipse.

+0

Como testimonio personal, he experimentado un problema similar al que tenía el OP (mi aplicación arrojaba excepciones porque no podía abrir más archivos ya que tenía demasiados descriptores de archivos abiertos). Ejecutar el código a través de findbugs ayudó a identificar todos los lugares donde no se cerraron los archivos. ¡Problema resuelto! – tth

+0

Sí, una vez me ayudó a encontrar una gran cantidad de lugares donde close() no había sido llamado en un bloque finally apropiado. – Benj

+0

aunque no resolvió mi problema directamente, ¡fue una gran pista! – dlinsin

0

Sin duda podría darle una idea. Como es Java, la mecánica de abrir/cerrar archivos debe implementarse de manera similar (a menos que una de las JVM se implemente incorrectamente). Yo recomendaría usar File Monitor en Windows.

1

Comenzaría pidiéndole a mi administrador de sistemas que obtenga una lista de todos los descriptores de archivos abiertos para el proceso. Diferentes sistemas hacen esto de diferentes maneras: Linux, por ejemplo, tiene el directorio /proc/PID/fd. Recuerdo que Solaris tiene un comando (¿quizás pfiles?) Que hará lo mismo: su administrador de sistemas debería saberlo.

Sin embargo, a menos que vea muchas referencias al mismo archivo, una lista de fd no lo ayudará. Si se trata de un proceso de servidor, probablemente tenga muchos archivos (y sockets) abiertos por alguna razón. La única forma de resolver el problema es ajustar el límite del sistema en archivos abiertos; también puede verificar el límite por usuario con ulimit, pero en la mayoría de las instalaciones actuales es igual al límite del sistema.

7

En las ventanas se puede mirar de archivo abierto mediante el proceso de explorador:

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

En Solaris puede utilizar "lsof" para supervisar el archivo abierto

+0

¡Gracias! Usé lsof, desafortunadamente hay muchas cosas sucediendo y realmente no sé cómo reducir los resultados de lsof a lo que es relevante y lo que no es interesante. – dlinsin

+1

El resultado de Windows no se debe extrapolar a los sistemas * nix. Tienen un mecanismo diferente para abrir archivos. –

1

No es una respuesta directa a su pregunta, pero estos problemas podrían ser el resultado de liberar recursos de archivos incorrectamente en su código heredado. Por ejemplo, si está trabajando con las clases FileOutputsStream asegurarse de que los métodos de cierre se llaman en un bloque finally como en este ejemplo:

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

Lo que dijo. suena como que los identificadores de archivo nunca se lanzan. – ChadNC

+0

Gracias por el consejo general, pero he buscado todas las apariciones de java.io. * y me he asegurado de que estén en un bloque try-catch-finally. – dlinsin

2

responder a la segunda parte de la pregunta:

lo que puede causar de archivo abierto a agotarse?

Abrir muchos archivos, obviamente, y luego no cerrarlos.

El caso más simple es que las referencias a los objetos que contienen los identificadores nativos (por ejemplo, FileInputStream) se descartan antes de cerrarse, lo que significa que los archivos permanecen abiertos hasta que finalizan.

La otra opción es que los objetos se almacenan en algún lugar y no se cierran. Un volcado de pila podría indicarle qué restos (jmap y jhat están incluidos en el JDK, o puede usar jvisualvm si desea una GUI). Probablemente esté interesado en buscar objetos que posean FileDescriptor s.

2

Este pequeño script me ayuda a controlar la cantidad de archivos abiertos cuando necesito un conteo de prueba. Si se ha utilizado en Linux, así que para Solaris debe parche (puede ser :))

#!/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 

También se puede tratar de jugar con JVM ontion -Xverify: ninguno - se debe desactivar la verificación vaso (si es más de los archivos abiertos son tarros ...). Para las filtraciones a través de FileOutputStream no cerrado, puede utilizar findbug (aconsejado anteriormente) o tratar de encontrar el artículo sobre parches de java estándar FileOutputStream/FileInputStream, donde puede ver, quién abre archivos y se olvidó de cerrarlos. Desafortunadamente, no puede encontrar este artículo en este momento, pero esto existe :) También piense en aumentar el límite de archivos - para kernels * nix actualizados no es un problema manejar más de 1024 fd.

2

Esto puede no ser práctico en su caso, pero lo que hice una vez cuando tuve un problema similar con las conexiones de bases de datos abiertas fue anular la función "abrir" con la mía. (Convenientemente ya tenía esta función porque habíamos escrito nuestra propia agrupación de conexiones). En mi función, agregué una entrada a una tabla que registraba la apertura. Hice una llamada de seguimiento de pila y guardé la identificación de la persona que llama, junto con la hora de llamada y olvido qué más. Cuando se lanzó la conexión, borré la entrada de la tabla. Luego tuve una pantalla donde podíamos volcar la lista de entradas abiertas. Luego podría ver la marca de tiempo y ver fácilmente qué conexiones se han abierto durante cantidades de tiempo poco probables y qué funciones se han abierto.

De esto, pudimos rastrear rápidamente las dos funciones que estaban abriendo conexiones y no cerrarlas.

Si tiene muchos identificadores de archivo abiertos, lo más probable es que no los cierre cuando termine en algún lugar. Dice que ha comprobado si hay bloques de try/finally correctos, pero sospecho que en alguna parte del código se ha saltado una mala, o tiene una función que tiene y nunca llega al final. Supongo que también es posible que realmente estés cerrando correctamente cada vez que abres un archivo, pero estás abriendo cientos de archivos simultáneamente. Si ese es el caso, no estoy seguro de qué puedes hacer, aparte de un rediseño serio del programa para manipular menos archivos, o un rediseño serio del programa para poner en cola tus accesos de archivos. (En este punto agrego el habitual, "Sin conocer los detalles de su aplicación, etc.)

1

Verificaría dos veces la configuración del entorno en su cuadro de Solaris. Creo que de forma predeterminada Solaris solo permite 256 identificadores de archivo por Para una aplicación de servidor, especialmente si se ejecuta en un servidor dedicado, esto es muy bajo. Figura 50 o más descriptores para abrir JRE y JAR de biblioteca, y luego al menos un descriptor para cada solicitud entrante y consulta de base de datos, probablemente más, y se puede ver cómo esto simplemente no se corte la mostaza para un servidor serio.

Tener un vistazo al archivo /etc/system, para los valores de rlim_fd_cur y rlim_fd_max, para ver lo que tu sistema ha establecido Luego considere si esto es razonable (puede ver cuántos descriptores de archivos están abiertos mientras el servidor se ejecuta con el comando lsof, idealmente con el parámetro -p [ID de proceso].

2

Vale la pena tener en cuenta que también consume identificadores de archivos en sistemas Unix.Así que podría ser algo así como una fuga de grupo de conexión de base de datos (por ejemplo, conexiones de bases de datos abiertas que no se cierran y devuelven al grupo) que está conduciendo a este problema, ciertamente he visto este error antes causado por una fuga de grupo de conexión.

0

Google para una aplicación llamada filemon desde el sistema interno.

BTW, para rastrear esto puede utilizar algo como aspectj para registrar todas las llamadas que abren y cierran archivos y registra donde ocurren.

+0

Y eso fue rechazado porque? – vickirk

0

Este es un patrón de codificación que ayuda a encontrar recursos no cerrados. Cierra los recursos y también se queja en el registro sobre el 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(); 
    } 
} 

Envuelva las llamadas file.close() anteriores en los bloques try-catch que ignoran los errores.

Además, Java 7 tiene una nueva función 'probar con recursos' que puede cerrar automáticamente los recursos.

+0

Su diseño realmente malo para usar finalize() http://www.informit.com/articles/article.aspx?p=1216151&seqNum=7 –

Cuestiones relacionadas