rails multi locales language i18n ruby memory memory-management coding-style

ruby - multi - rails translation interpolation



Ruby Memory Management (5)

Al trabajar con enormes matrices de objetos ActiveRecord, tenga mucho cuidado ... Al procesar esos objetos en un bucle, si en cada iteración está cargando sus objetos relacionados utilizando has_many, belongs_to, etc., de ActiveRecord, el uso de memoria crece mucho porque cada objeto que pertenece a una matriz crece ...

La siguiente técnica nos ayudó mucho ( ejemplo simplificado ):

students.each do |student| cloned_student = student.clone ... cloned_student.books.detect {...} ca_teachers = cloned_student.teachers.detect {|teacher| teacher.address.state == ''CA''} ca_teachers.blah_blah ... # Not sure if the following is necessary, but we have it just in case... cloned_student = nil end

En el código anterior, "cloned_student" es el objeto que crece, pero como está "anulado" al final de cada iteración, esto no es un problema para una gran variedad de estudiantes. Si no hiciéramos "clonar", la variable de bucle "estudiante" habría crecido, pero dado que pertenece a una matriz, la memoria utilizada nunca se libera mientras exista el objeto de matriz.

Otro enfoque también funciona:

students.each do |student| loop_student = Student.find(student.id) # just re-find the record into local variable. ... loop_student.books.detect {...} ca_teachers = loop_student.teachers.detect {|teacher| teacher.address.state == ''CA''} ca_teachers.blah_blah ... end

En nuestro entorno de producción, tuvimos un proceso en segundo plano que no terminó una vez porque 8 GB de RAM no eran suficientes. Después de este pequeño cambio usa menos de 1 Gb para procesar la misma cantidad de datos ...

He estado usando Ruby por un tiempo y encuentro que, para proyectos más grandes, puede ocupar una buena cantidad de memoria. ¿Cuáles son algunas de las mejores prácticas para reducir el uso de memoria en Ruby?

  • Por favor, deje que cada respuesta tenga una "mejor práctica" y permita que la comunidad la vote.

No abuse de los símbolos.

Cada vez que creas un símbolo, ruby ​​pone una entrada en su tabla de símbolos. La tabla de símbolos es un hash global que nunca se vacía.
Esto técnicamente no es una pérdida de memoria, pero se comporta como uno. Los símbolos no ocupan mucha memoria, por lo que no es necesario ser demasiado paranoico, pero vale la pena tener esto en cuenta.

Una guía general: si realmente ha escrito el símbolo en el código, está bien (después de todo, solo tiene una cantidad finita de código), pero no llame a to_sym en las cadenas generadas dinámicamente o de entrada del usuario, ya que esto abre la puerta a un número potencialmente creciente


No hagas esto:

def method(x) x.split( doesn''t matter what the args are ) end

o esto:

def method(x) x.gsub( doesn''t matter what the args are ) end

Ambos perderán permanentemente memoria en ruby ​​1.8.5 y 1.8.6 . (No estoy seguro acerca de 1.8.7 ya que no lo he probado, pero realmente espero que esté solucionado). La solución alternativa es estúpida e implica la creación de una variable local. No tienes que usar el local, solo crea uno ...

Cosas como esta son las razones por las que tengo mucho amor por el lenguaje ruby, pero no respeto por la resonancia magnética


Tenga cuidado con las extensiones C que asignan trozos grandes de memoria.

Como ejemplo, cuando carga una imagen usando RMagick, todo el mapa de bits se carga en la memoria dentro del proceso ruby. Esto puede ser de 30 meg más o menos dependiendo del tamaño de la imagen.
Sin embargo , la mayor parte de esta memoria ha sido asignada por RMagick. Todo lo que Ruby sabe es un objeto envoltorio, que es pequeño (1).
Ruby solo piensa que está reteniendo una pequeña cantidad de memoria, por lo que no molestará en ejecutar el GC. De hecho, se mantiene en 30 megas.
Si coloca un bucle sobre un valor de 10 imágenes, puede salir corriendo de la memoria muy rápido.

La solución preferida es decirle manualmente a la biblioteca C que limpie la memoria en sí: ¡RMagick tiene un destructor! método que hace esto. Sin embargo, si su biblioteca no funciona, es posible que deba ejecutar el GC a la fuerza, aunque esto generalmente no se recomienda.

(1): las extensiones de Ruby C tienen devoluciones de llamada que se ejecutarán cuando el tiempo de ejecución de ruby ​​decida liberarlas, por lo que la memoria finalmente se liberará con éxito en algún momento, tal vez no lo suficientemente pronto.


Mida y detecte qué partes de su código están creando objetos que hacen que el uso de la memoria aumente . Mejora y modifica tu código y luego mide nuevamente. A veces, estás utilizando gemas o bibliotecas que consumen mucha memoria y crean muchos objetos también.

Existen muchas herramientas como busy-administrator que le permiten verificar el tamaño de la memoria de los objetos (incluidos los hashes y matrices).

$ gem install busy-administrator

Ejemplo n. ° 1: MemorySize.of

require ''busy-administrator'' data = BusyAdministrator::ExampleGenerator.generate_string_with_specified_memory_size(10.mebibytes) puts BusyAdministrator::MemorySize.of(data) # => 10 MiB

Ejemplo n. ° 2: MemoryUtils.profile

Código

require ''busy-administrator'' results = BusyAdministrator::MemoryUtils.profile(gc_enabled: false) do |analyzer| BusyAdministrator::ExampleGenerator.generate_string_with_specified_memory_size(10.mebibytes) end BusyAdministrator::Display.debug(results)

Salida:

{ memory_usage: { before: 12 MiB after: 22 MiB diff: 10 MiB } total_time: 0.406452 gc: { count: 0 enabled: false } specific: { } object_count: 151 general: { String: 10 MiB Hash: 8 KiB BusyAdministrator::MemorySize: 0 Bytes Process::Status: 0 Bytes IO: 432 Bytes Array: 326 KiB Proc: 72 Bytes RubyVM::Env: 96 Bytes Time: 176 Bytes Enumerator: 80 Bytes } }

También puedes probar ruby-prof y memory_profiler . Es mejor si prueba y experimenta diferentes versiones de su código para que pueda medir el uso de la memoria y el rendimiento de cada versión. Esto le permitirá verificar si su optimización realmente funcionó o no. Por lo general, utiliza estas herramientas en el modo de desarrollo / prueba y las desactiva en producción.