2009-06-06 21 views
14

Estoy desarrollando una aplicación web con Merb y estoy buscando una biblioteca de procesamiento de imágenes segura y estable. Solía ​​trabajar con Imagick en php, luego me mudé a ruby ​​y comencé a usar RMagick. Pero hay un problema. Guiones de ejecución prolongada que causan pérdidas de memoria. Hay una solución de pareja, pero no sé cuál es la más estable. ¿Entonces, qué piensas?¿Cómo lidiar con fugas de memoria en RMagick en Ruby?

En este momento, mi aplicación utiliza API interna que escribí para procesar imágenes, en PHP. Se ejecuta en un servidor separado junto con otras aplicaciones, por lo que no es un gran problema. Pero creo que no es una buena arquitectura.

De todos modos, consideraré cualquier consejo práctico.

Respuesta

11

Yo también he encontrado este problema: la solución es forzar la recolección de basura.

Cuando haya reasignado la variable de imagen a una nueva imagen, simplemente use GC.start para asegurarse de que la referencia anterior se libera de la memoria.

En las versiones posteriores de RMagick, también creo que también puede llamar a destroy! en la imagen cuando haya terminado de procesarlo.

Una combinación de los dos probablemente garantizaría que esté cubierto, pero no estoy seguro del impacto de la vida real en el rendimiento (supongo que es insignificante en la mayoría de los casos).

Como alternativa, puede usar mini-magick que es un contenedor para el cliente de línea de comandos ImageMagick.

+1

sí, fue una de las soluciones de las que me he enterado. pero desde el otro lado, llamar a GC todo el tiempo no es una buena idea (vi un artículo sobre esto hace un tiempo). puede causar desaceleración. y también, gc es una operación muy 'costosa'. im no estoy seguro, pero ahora no tengo opciones. también, hay una versión mejorada de rmagick, pero aún así, tiene pérdidas de memoria, solo es cuestión de tiempo –

+0

Mi consejo sería hacer un perfil de la recolección de basura y ver si puede lidiar con ella. Una alternativa sería ImageScience (http://seattlerb.rubyforge.org/ImageScience.html) pero no es tan capaz como RMagick. – Matt

+4

Al llamar a Image # destroy! en imágenes intermedias, pude reducir el uso de la memoria en un orden de magnitud en mi aplicación Rails vinculada a RMagick, desde 200MB hasta> 40MB. El truco consistía en mantener el número de objetos Magick :: Image/Magick :: ImageList en la memoria RAM al mismo tiempo lo más bajo posible. Llamar a GC.start no fue necesario. – foz

-1

Esto no se debe a ImageMagick; se debe a Ruby, y es un problema bien conocido. Mi sugerencia es dividir su programa en dos partes: una parte de larga duración que asigna poca memoria y solo trata con el control del sistema, y ​​un programa separado que realmente hace el trabajo de procesamiento. El proceso de control a largo plazo debería hacer lo suficiente para encontrar trabajo para un proceso hijo que genera, y el niño debe hacer todo el procesamiento para ese elemento de trabajo en particular.

Otra opción sería dejar los dos combinados, pero después de completar una unidad de trabajo, use exec para reemplazar su proceso con una versión recién iniciada del mismo programa, que buscaría otro elemento de trabajo, procesarlo y ejecutar a sí mismo de nuevo.

Esto supone que los elementos de trabajo son bastante grandes, lo que es casi seguro si está utilizando ImageMagick. Si no lo son, descubrirá que la sobrecarga de generar un nuevo proceso y hacer que el intérprete de Ruby vuelva a analizar todo el programa comienza a ser demasiado grande. Puede hacer frente a esto haciendo que su programa haga más unidades de trabajo (digamos, diez o cien) antes de volver a ejecutarse.

+0

como dije en la publicación original, actualmente estoy usando una pieza externa del programa que se implementó como API. –

+1

Sí, ahora veo. Supongo que mi consejo sigue siendo el mismo, excepto que podrías interpretarlo como "sigue con eso" ... –

5

En realidad, no es realmente un problema específico de Ruby, otros intérpretes lo comparten también. El problema concreto es que el GC de Ruby solo ve la memoria asignada por Ruby, y no por bibliotecas externas (con la notable excepción de la biblioteca que usa las funciones de administración de memoria de Rubys). Por lo tanto, un objeto ImageMagick-Object en el espacio de memoria de Ruby es muy pequeño, pero la imagen en el espacio administrado por ImageMagick es grande. Entonces, esto no es una filtración per se, pero se comporta como uno. Rubys Garbage Collector nunca se activa si su proceso se mantiene por debajo de un cierto límite (8 MB es estándar). Como ImageMagick nunca crea objetos grandes en el espacio de Ruby, probablemente nunca se active. Entonces, o bien utiliza el método propuesto para generar un nuevo proceso o usa el comando exec. Otra bastante ingeniosa es tener un servicio de procesamiento de imágenes en el backend que se bifurca para cada tarea. Otra sería tener algún tipo de monitoreo en el lugar que inicie el GC de vez en cuando.

Hay otra biblioteca llamada MagickWand por Timothy Paul Hunter (el autor de RMagick) que intenta solucionar estos problemas y crear una API más agradable. Sin embargo, está en alfa y requiere una versión bastante nueva de ImageMagick.

+0

Bueno, basado en el comentario de mi respuesta, podría estar equivocado sobre los detalles de esta situación. Pero estoy razonablemente seguro al decir que si hay una fuga, es mucho más probable en Ruby que en una biblioteca bien utilizada como ImageMagick. (Bueno, está bien, también existe la posibilidad de que esté relacionado con la interfaz entre los dos, pero eso cuenta como Ruby para mí). Pero creo que su caracterización general del comportamiento del GC es incorrecta. Siempre que el código de pegamento que se conecta con la biblioteca administre la memoria de manera apropiada, la GC debe considerar lo que la biblioteca asigna. –

+1

Como dije: no es una filtración por definición, aunque se comporta como tal. Si el GC desactiva el objeto, la memoria se libera correctamente. El punto es que el GC nunca ve suficiente memoria asignada para siquiera funcionar. Y Rubys GC es realmente conservador cuando se inicia, especialmente el hecho de que nunca entra en acción siempre que el tamaño de la memoria vista sea menor a cierto tamaño. Tiene razón: esta es una biblioteca bien diseñada que asegura que toda la memoria asignada pueda ser vista por ruby ​​corrige este problema, esto es básicamente lo que MagickWand está haciendo. – Skade

6

Al usar RMagick es importante recordar destruir la imagen una vez que haya terminado, de lo contrario, se llenará el directorio/tmp cuando se trabaja con grandes conjuntos de imágenes. Por ejemplo, debes llamar a destruir!

require 'RMagick' 

Dir.foreach('/home/tiffs/') do |file| 
    next if file == '.' or file == '..' 
     image = Magick::Image.read(file).first 
     image.format = "PNG" 
     image.write("/home/png/#{File.basename(file, '.*')}.png") 
     image.destroy! 
end