2010-07-15 9 views
9

Estoy ejecutando un servidor Perl con 10 subprocesos. Nunca se destruyen hasta que el programa se cierra, pero esto es algo que pretendo tener el mayor tiempo de actividad posible, por eso es un problema para mí. Los hilos manejan una tarea simple muchas veces. Cuando inicio el servidor y todos los hilos se inician, veo que tengo 288.30 MB gratis. Después de un par de iteraciones a través de cada hilo, informa 285.96 MB gratis. Eso no es tan malo ... quizás sea solo un espacio de pila asignado o algo durante esas iteraciones. ¡Pero después de 15 minutos, la memoria libre baja a 248.24 MB! ¿Qué pasó con mi memoria? Ahora, curiosamente, HACE meseta. Continúa consumiendo lentamente, pero no tan rápido como al principio. Pensé que tal vez era mi culpa, así que intenté verificar el alcance de todas mis variables e incluso definirlas todas al final del ciclo.Los subprocesos Perl consumen memoria lentamente

Imprimo el espacio libre después de CADA iteración de los hilos para que pueda ver cómo baja lentamente. Ahora lo que también es interesante es que no disminuye cada vez. A veces, la memoria libre se mantiene igual después de iterar.

estoy usando Perl 5.8.8 construida desde las fuentes en Linux 2.6

¿Alguien tiene alguna idea en absoluto, o incluso sugerencias sobre lo que puede ser la causa de esto? Estoy considerando actualizar mi Perl a una versión posterior para descartar una pérdida de memoria dentro del núcleo de Perl.

ACTUALIZACIÓN: ¿Podría ser un problema de tamaño de la pila de subprocesos? ¿Podría asignar más memoria para la pila de la que necesito? Cuando creo mis hilos, no cambio la configuración de los valores predeterminados. ¿Debería? El documento de hilos dice que el valor predeterminado generalmente es de 16 MB dependiendo del sistema. 16x10 hilos = 160MB -> que podría ser el culpable. ¿Pensamientos?

ACTUALIZACIÓN: Creé e instalé Perl 5.12.1 y reconstruí los módulos y todo. He estado ejecutando el script durante aproximadamente una hora y esto es lo que noté. El uso de la memoria es manejable ahora, pero no es ideal.

  • Al principio justo después del desove mis hilos parecían ser un poco más bajos. Abajo de ~ 60-66MB asignado a mis 10 hilos a ~ 45-50MB.
  • Después de algunas iteraciones, su uso aumentó en 3 MB en total (casi lo mismo que antes).
  • Hasta este punto es lo que esperaba. Toda esa memoria por adelantado para el desove, y luego solo un poco por las variables que uso en mis hilos. Esta es la parte que no me gusta. ¡Después de correr durante aproximadamente 10 minutos, pierdo 65 MB adicionales! ¿Por qué hace esto? Si ya se ha repetido algunas veces bien con solo 3 MB, ¿por qué seguir asignando?
  • Ha estado funcionando durante una hora y media en este punto y ya no usan 65MB adicionales, ¡son 84MB adicionales!
  • Poco a poco lleva más memoria, pero lo extraño es que la cantidad de memoria libre no disminuye en cada iteración. Imprimo la memoria libre antes y después de cada iteración y permanecerá igual por un tiempo o desplazarme + - alrededor de cierto número por un tiempo y luego cambiar de 5 a 10 MB de repente. No puedo dejar que esto funcione más de uno, dos días como máximo porque comienza a acercarse al 80/90% de mi memoria disponible.

¿Hay alguna otra idea? ¿Alguna cosa que pueda probar? Ya estoy descifrando todas mis variables.

ACTUALIZACIÓN: Realmente quiero seguir compilando Perl con glibc como último recurso, ya que he encontrado algunos informes de que en algunos sabores de Linux se segfault.Así que desde la última vez que publiqué exploré aún más la posibilidad de ciclos en mis valores hash. Encontrar nada. Así que pasé los últimos días analizando mi subrutina y guardando en caché todo lo que se usa en otra iteración. Se recrean muchas cosas nuevas cada vez y Perl no las limpia todo, incluso si no lo explico explícitamente. Entonces, si no coopera, simplemente no lo destruiré. Veré si el almacenamiento en caché de mis objetos ayuda en absoluto. Publicará estadísticas de uso de memoria más tarde.

ACTUALIZACIÓN: Hm, muy extraño. Incluso después de guardar en caché mis datos para volver a usarlos más adelante, la memoria aumenta aproximadamente a la misma velocidad. Empieza más alto ahora porque estoy almacenando en caché, pero luego sigue aumentando a pesar de que usa principalmente mis objetos en caché. Eso es desconcertante. Supongo que es hora de probar Glibc ... o bien, esto es solo un inconveniente de elegir Perl y tendrá que vivir reiniciando el servidor cada dos días.

ACTUALIZACIÓN: Probado sin el almacenamiento en caché, no glibc, de nuevo. Funciona bien por un tiempo, unas pocas horas, luego comienza a crecer. Solo quería que veas un gráfico.
http://tinypic.com/r/311nc08/3
http://i32.tinypic.com/311nc08.jpg

UPDATE: He aquí un extracto de un registro de la documentación de la memoria libre antes y después de cada hilo por encima de aproximadamente un minuto. Tal vez esto puede ayudar a alguien a entender mejor el problema. Parece ser estable por un tiempo y de vez en cuando comerá más memoria como esta. ¡Aquí pierdo casi 40 MB!

[9:8:30, Fri Jul 23, 2010] [0] Memory usage at end thread 1: 253.812736MB (obj cache: 136) 
[9:8:30, Fri Jul 23, 2010] [0] Memory usage at idle thread 1: 253.812736MB (obj cache: 136) 
[9:8:34, Fri Jul 23, 2010] [204] Sending data to thread 
[9:8:34, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:8:34, Fri Jul 23, 2010] [206] Sending data to thread 
[9:8:34, Fri Jul 23, 2010] [0] 4 - Creating a new obj 
[9:8:35, Fri Jul 23, 2010] [0] Memory usage at end thread 3: 253.812736MB (obj cache: 136) 
[9:8:35, Fri Jul 23, 2010] [0] Memory usage at idle thread 3: 253.812736MB (obj cache: 136) 
[9:8:35, Fri Jul 23, 2010] [0] Memory usage at end thread 4: 253.812736MB (obj cache: 136) 
[9:8:35, Fri Jul 23, 2010] [0] Memory usage at idle thread 4: 253.812736MB (obj cache: 136) 
[9:8:41, Fri Jul 23, 2010] [225] Sending data to thread 
[9:8:41, Fri Jul 23, 2010] [0] 2 - Creating a new obj 
[9:8:42, Fri Jul 23, 2010] [0] Memory usage at end thread 2: 253.681664MB (obj cache: 136) 
[9:8:42, Fri Jul 23, 2010] [0] Memory usage at idle thread 2: 253.681664MB (obj cache: 136) 
[9:8:47, Fri Jul 23, 2010] [243] Sending data to thread 
[9:8:47, Fri Jul 23, 2010] [0] 1 - Creating a new obj 
[9:8:48, Fri Jul 23, 2010] [0] Memory usage at end thread 1: 253.935616MB (obj cache: 136) 
[9:8:48, Fri Jul 23, 2010] [0] Memory usage at idle thread 1: 253.935616MB (obj cache: 136) 
[9:9:1, Fri Jul 23, 2010] [277] Sending data to thread 
[9:9:1, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:9:2, Fri Jul 23, 2010] [280] Sending data to thread 
[9:9:2, Fri Jul 23, 2010] [0] 4 - Creating a new obj 
[9:9:2, Fri Jul 23, 2010] [0] Memory usage at end thread 3: 253.935616MB (obj cache: 136) 
[9:9:2, Fri Jul 23, 2010] [0] Memory usage at idle thread 3: 253.935616MB (obj cache: 136) 
[9:9:3, Fri Jul 23, 2010] [283] Sending data to thread 
[9:9:3, Fri Jul 23, 2010] [0] 2 - Creating a new obj 
[9:9:4, Fri Jul 23, 2010] [284] Sending data to thread 
[9:9:4, Fri Jul 23, 2010] [0] 1 - Creating a new obj 
[9:9:4, Fri Jul 23, 2010] [0] Memory usage at end thread 2: 253.935616MB (obj cache: 136) 
[9:9:4, Fri Jul 23, 2010] [0] Memory usage at idle thread 2: 253.935616MB (obj cache: 136) 
[9:9:5, Fri Jul 23, 2010] [287] Sending data to thread 
[9:9:5, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:9:5, Fri Jul 23, 2010] [0] Memory usage at end thread 4: 253.93152MB (obj cache: 136) 
[9:9:5, Fri Jul 23, 2010] [0] Memory usage at idle thread 4: 253.93152MB (obj cache: 136) 
[9:9:6, Fri Jul 23, 2010] [290] Sending data to thread 
[9:9:6, Fri Jul 23, 2010] [0] 2 - Creating a new obj 
[9:9:7, Fri Jul 23, 2010] [0] Memory usage at end thread 3: 253.804544MB (obj cache: 136) 
[9:9:7, Fri Jul 23, 2010] [0] Memory usage at idle thread 3: 253.804544MB (obj cache: 136) 
[9:9:7, Fri Jul 23, 2010] [0] Memory usage at end thread 1: 253.804544MB (obj cache: 136) 
[9:9:7, Fri Jul 23, 2010] [0] Memory usage at idle thread 1: 253.804544MB (obj cache: 136) 
[9:9:9, Fri Jul 23, 2010] [0] 4 - Creating a new obj 
[9:9:9, Fri Jul 23, 2010] [301] Sending data to thread 
[9:9:9, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:9:9, Fri Jul 23, 2010] [302] Sending data to thread 
[9:9:9, Fri Jul 23, 2010] [0] 1 - Creating a new obj 
[9:9:10, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:9:11, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:9:11, Fri Jul 23, 2010] [0] Memory usage at end thread 4: 253.93152MB (obj cache: 136) 
[9:9:11, Fri Jul 23, 2010] [0] Memory usage at idle thread 4: 253.93152MB (obj cache: 136) 
[9:9:12, Fri Jul 23, 2010] [308] Sending data to thread 
[9:9:12, Fri Jul 23, 2010] [0] 4 - Creating a new obj 
[9:9:13, Fri Jul 23, 2010] [0] Memory usage at end thread 1: 253.804544MB (obj cache: 136) 
[9:9:13, Fri Jul 23, 2010] [0] Memory usage at idle thread 1: 253.804544MB (obj cache: 136) 
[9:9:14, Fri Jul 23, 2010] [0] Memory usage at end thread 4: 253.804544MB (obj cache: 136) 
[9:9:14, Fri Jul 23, 2010] [0] Memory usage at idle thread 4: 253.804544MB (obj cache: 136) 
[9:9:14, Fri Jul 23, 2010] [0] Memory usage at end thread 3: 253.93152MB (obj cache: 136) 
[9:9:14, Fri Jul 23, 2010] [0] Memory usage at idle thread 3: 253.93152MB (obj cache: 136) 
[9:9:15, Fri Jul 23, 2010] [313] Sending data to thread 
[9:9:15, Fri Jul 23, 2010] [0] 1 - Creating a new obj 
[9:9:16, Fri Jul 23, 2010] [0] Memory usage at end thread 2: 214.482944MB (obj cache: 136) 
[9:9:16, Fri Jul 23, 2010] [0] Memory usage at idle thread 2: 214.482944MB (obj cache: 136) 
[9:9:16, Fri Jul 23, 2010] [315] Sending data to thread 
[9:9:16, Fri Jul 23, 2010] [0] 4 - Creating a new obj 
[9:9:17, Fri Jul 23, 2010] [0] Memory usage at end thread 1: 214.355968MB (obj cache: 136) 
[9:9:17, Fri Jul 23, 2010] [0] Memory usage at idle thread 1: 214.355968MB (obj cache: 136) 
[9:9:18, Fri Jul 23, 2010] [316] Sending data to thread 
[9:9:18, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:9:18, Fri Jul 23, 2010] [317] Sending data to thread 
[9:9:18, Fri Jul 23, 2010] [0] 2 - Creating a new obj 
[9:9:18, Fri Jul 23, 2010] [318] Sending data to thread 
[9:9:18, Fri Jul 23, 2010] [0] 1 - Creating a new obj 
[9:9:19, Fri Jul 23, 2010] [0] Memory usage at end thread 4: 214.355968MB (obj cache: 136) 
[9:9:19, Fri Jul 23, 2010] [0] Memory usage at idle thread 4: 214.355968MB (obj cache: 136) 
[9:9:19, Fri Jul 23, 2010] [0] Memory usage at end thread 1: 214.355968MB (obj cache: 136) 
[9:9:19, Fri Jul 23, 2010] [0] Memory usage at idle thread 1: 214.355968MB (obj cache: 136) 
[9:9:20, Fri Jul 23, 2010] [0] Memory usage at end thread 3: 214.482944MB (obj cache: 136) 
[9:9:20, Fri Jul 23, 2010] [0] Memory usage at idle thread 3: 214.482944MB (obj cache: 136) 
[9:9:20, Fri Jul 23, 2010] [0] Memory usage at end thread 2: 214.482944MB (obj cache: 136) 
[9:9:20, Fri Jul 23, 2010] [0] Memory usage at idle thread 2: 214.482944MB (obj cache: 136) 

ACTUALIZACIÓN (8/12/2010): Sólo dirigió durante un día con una nueva versión compilada de Perl 5.12 con hilos y malloc sistema. Extrañamente, tengo el mismo comportamiento. Pierde trozos de MB a la vez, lentamente. Podría intentar Valgrind para ver por qué lo estoy perdiendo. Mientras estaba jugando con otra cosa, pensé en otra cosa. Mi script crea y destruye (supuestamente) muchos muchos sockets SSL. ¿Es posible que un módulo ampliamente utilizado como IO :: Socket :: SSL gotee un poco? ¿O tal vez OpenSSL? (Usando v0.9.8o). Intentando sincronizar el acceso al módulo SSL para ver si tiene algún efecto, puede haber problemas con los subprocesos que acceden a él.

ACTUALIZACIÓN: Se intentó cargar los módulos por separado dentro de cada hilo, un uso más rápido de la memoria. Intentó bloquear las áreas usando las funciones del socket para que solo un hilo a la vez las usara, aún así perdió la memoria de la misma manera que antes. Aumentó la cantidad de hilos de trabajo de 4 a 10 con la misma cantidad de trabajo. La memoria no duró 30 minutos. Me lleva a creer que es un problema de Perl internamente con su implementación de subproceso, o un problema de pila (sin juego de palabras intencionado). Traté de cambiar el tamaño de la pila usando los métodos de subprocesos incorporados pero el mismo resultado. Ir a buscar otra forma. Tal vez una forma de menor nivel. El aumento del número de hilos hace que la memoria ir más rápido ... parece ser algo con la implementación de pila los hilos o el tamaño de la pila

ACTUALIZACIÓN (9/15/2010): ha encontrado interesante esta golosina en el IO :: Socket :: SSL doc ...

Esto se debe al hecho de que se necesita una referencia circular para hacer que IO :: Socket :: SSL sockets actúen simultáneamente como objetos y referencias globales.

"Referencia circular" ¿eh? Otra explicación posible es que estos enchufes se mantienen por un tiempo aunque los descifre explícitamente. Ir a buscar en Weaken para ver si eso hace algo con los enchufes. Te dejaré saber si encuentro algo interesante.

SOLUCIONADO (9/16/2010): Véase mi respuesta que he publicado que contiene la solución

+0

Publique un código o busque referencias circulares: P –

+1

¿Ha mirado esta pregunta: http://stackoverflow.com/questions/429254/how-can-i-find-memory-leaks-in-long-running -perl-program – dwarring

+0

posible duplicado de [Perl de perfiles de uso de memoria y detección de fugas?] (http://stackoverflow.com/questions/1359771/perl-memory-usage-profiling-and-leak-detection) – Ether

Respuesta

5

Finalmente identifiqué la fuga. Primero, me gustaría mostrarte la mejoría. No debe ver los números reales porque la base de usuarios ha aumentado desde que se tomó el primer gráfico, solo observe la diferencia en la pendiente.

El antes gráfico de uso de la memoria: http://i32.tinypic.com/311nc08.jpg
El después gráfico de uso de la memoria: http://i51.tinypic.com/29goill.jpg

Durante muchos meses, me he quedado atrapado reiniciar el servidor cada par de días, pero durante los últimos 14 horas, no ha habido un aumento de uso de memoria.

Cada tutorial, ejemplo, presentación y libro que utilicé para ayudar a desarrollar el servidor, todos omitieron un hecho muy importante con respecto a IO :: Socket :: SSL. Y todos los que utilizan ese módulo dentro de una aplicación con subprocesos mejor escuchan. Nadie destacó nunca una de las últimas líneas en la documentación de IO :: Socket :: SSL, que me llevó a suponer tontamente que cualquier socket que crease, como casi cualquier otra estructura de datos, se liberaría una vez que salga del alcance (Y sí , Sé las excepciones a esa regla).Pensé que haría un favor a todos y llamar a la línea a la que me refiero.

... IO :: Socket :: Los sockets SSL permanecerán abiertos hasta que el programa finalice o los cierre explícitamente. Esto se debe al hecho de que se requiere una referencia circular para hacer que IO :: Socket :: SSL sockets actúen simultáneamente como objetos y referencias globales.
http://search.cpan.org/dist/IO-Socket-SSL/SSL.pm#LIMITATIONS

nunca tuvo conocimiento del hecho de que estas tomas tenían una referencia circular en ellos, y si yo no sabía nada de ellos, a continuación, los blogs y libros de cada uno que lea tampoco lo saben (de ahí la llamada)

Así que como se puede imaginar, esto tenía una solución muy simple. Dentro del ciclo de trabajo de mi hilo, donde se crea un socket cada iteración, simplemente coloqué un eval { close $socket; };undef $socket; en la parte inferior para asegurar que se cierra antes de procesar el siguiente cliente. Encendí mi servidor y esperé y vi como el uso de la memoria era sólido, como puede ver en mi segundo gráfico. Así que después de dos meses de solucionar este problema de vez en cuando, finalmente llegué a una solución. Espero que esto proporcione alguna idea de la programación de cualquier otro aficionado con sockets. Gracias a todos los que proporcionaron respuestas/comentarios/sugerencias, cada poco ayudaron y ayudaron a tener un lugar desde el que intercambiar ideas.

+0

¡Felicitaciones! Me parece que el desarrollador de 'IO :: Socket :: SSL' debe 'sub CLONE {advertir' leer la sección de LIMITACIÓN de documentos '}' y solicitar que se desactive para desactivarlo. No veo ningún motivo por el cual se requiera una referencia circular. Apuesto a que esto fue escrito por un hacker de C que se fue perl, en lugar de un perl wiz. Presente un informe de error y presione para solucionarlo o agregue más comentarios. –

+0

Me gustaría agregar, esto suena mal citado, dice que si tienes 'Scalar :: Util' esto no sucederá. Y, tiene 'Scalar :: Util' si está usando' 5.8.8' - y especialmente si actualizó. Tal vez deberías traicionar a los idiotas en Red Hat o en cualquier distribución que uses que decidió enviar sin módulos principales ... O eso, o según tus números, el pod está equivocado. Todavía podría ver el módulo siendo más detallado sin embargo. –

+0

@Evan Instalé la distribución estándar desde la fuente, actualicé a 5.12.1. De hecho, viene con Scalar :: Util, pero por alguna razón no fue de ayuda. Traté de actualizar Scalar :: Util a la última versión y también instalé (como dice el documento también ayudará) WeakenRef. Todavía tenía el mismo problema. Muy extraño, pero finalmente solo cerrarlos manualmente antes de salir del alcance hizo el truco. Es por eso que dejé de lado esos módulos, porque no parecían ayudarme, pero sí, se supone que deben encargarse de eso. – casey

15

Su Perl es de 4 años y medio de edad. Actualiza a 5.12. Solo busca las notas 5.12 build y mirar el montón de mierda de las mejoras más importantes que tiene hilos que podría mágicamente arreglar el problema nebulosa:

  • 5,12 :: @_ y $ _ ya no fugas bajo roscas (RT # 34342 y # 41138, también # 70602, # 70974) '
  • 5.10 :: En ithreads, la expresión regular en PL_reg_curpm es ahora referencia contada. Esto elimina muchas soluciones provisionales para hacer frente a que no se cuente como referencia.
  • 5.9 :: hilos: Varias correcciones, por ejemplo, para problemas de unión() y pérdidas de memoria. En algunas plataformas (como Linux) que usan glibc, la huella de memoria mínima de un ithread se ha reducido en varios cientos de kilobytes.
  • 5.9 :: threads :: shared Se han corregido muchas pérdidas de memoria.

me refiero a la lista es interminable cuando se habla de cuatro años de desarrollo de la culata en T y la amplia gama de cosas que pueden causar este problema, echa un vistazo a la lista de cambios moderna de threads::shared

me comentó en su puesto , esta sería mi próxima serie de sugerencias: si no está utilizando glibc y está usando perl malloc (predeterminado), nunca liberará memoria en el sistema operativo. El tamaño del proceso representará el tamaño máximo permitido por cada uno. Intente reconstruir con glibc malloc (requiere recompilación) vea si eso proporciona un perfil de memoria diferente. Aparte de eso, será hora de mostrar el código.

+0

¿Por qué se bajó este voto? Con una pregunta como esa, esta respuesta podría ser útil. – Konerak

+1

Eso es útil. Descargué 5.10 en el servidor pero todavía no lo he creado. Creo que puedo obtener 5.12 y construir esa en su lugar ahora. Aún me pregunto si podría tratarse de un problema de tamaño de pila predeterminado de subproceso. – casey

+3

He estado ejecutando mi script ahora durante aproximadamente una hora en v5.12.1. Fue un gran paso en la dirección correcta, gracias. Publiqué mis resultados en la pregunta – casey

1

Intente actualizar threads.pm y threads :: shared. La actualización a Perl 5.12.1 también es una buena idea.

+0

Sí, actualicé esos módulos primero. No ayudó mucho. Acabo de reconstruir Perl 5.12.1 e instalé todos los módulos nuevamente. Esperará y verá cómo se ve el uso de la memoria en unas pocas horas – casey

1

Me encontré con el mismo problema con 5.10 y, a pesar de las numerosas protestas de que Perl más nuevo no perdía memoria cuando se usaban los subprocesos, Perl gastó memoria cuando se usaron los subprocesos.

Mi solución fue utilizar Thread::Pool::Simple para crear un grupo de subprocesos y usarlo en lugar de crear subprocesos nuevos. En mi caso, no esperaba tener muchas secuencias simultáneas ni durar mucho (tal vez 30 segundos como máximo). No sé si esta sería una opción para usted, pero si realmente solo hace que sus hilos manejen tareas simples, podría ser.

+0

Problema con eso es que estoy teniendo diez subprocesos simultáneos siempre en ejecución. No crearé ni destruiré ninguno después de los 10 iniciales. Por lo tanto, no necesito un objeto administrador de agrupaciones como Thread :: Pool. Pero, si lo que estás diciendo es que cambiar a Thread :: Pool te salvó de tener pérdidas de memoria, entonces lo intentaré. Mi segunda duda es que debo crear todos mis hilos al principio y no estoy seguro si esto me permite. Quizás tengas ese tipo de información. La memoria del proceso principal se vuelve demasiado grande y quiero mantener los hilos solo como trabajadores. – casey

+0

Mi situación tenía una gran cantidad de hilos que iban y venían, en lugar de persistir, por lo que lo que funcionó para mí podría no funcionar para usted. El problema que había notado era que la creación de subprocesos, incluso si no funcionaban y salían de inmediato, dejaba escapar un poco de memoria. Después de un tiempo, comienza a sumarse al uso real. No estoy muy familiarizado con la implementación de Thread :: Pool :: Simple pero tiene opciones 'min' y 'max' que me sugieren que crea subprocesos 'min' cuando inicia el grupo, pero no hacen ningún trabajo hasta que llamas al método 'agregar' del grupo. – Phil

Cuestiones relacionadas