Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby Memory Management

Tags:

I have been using Ruby for a while now and I find, for bigger projects, it can take up a fair amount of memory. What are some best practices for reducing memory usage in Ruby?

  • Please, let each answer have one "best practice" and let the community vote it up.
like image 879
Josh Moore Avatar asked Oct 08 '08 04:10

Josh Moore


People also ask

Does Ruby have automatic memory management?

Ruby, like most other modern, high-level programming languages, doesn't force you to manage memory. This feature is called garbage collection, or GC, and you get it for free in Ruby.

What causes Ruby memory bloat?

Slow Release. Another important cause of memory bloat in Ruby is a slow release of freed memory back to the system. In this situation, memory is freed much more slowly than the rate at which new memory blocks are allocated to objects.

Does Ruby have a GC?

Many modern programming languages manage memory for you, and Ruby is no different. Ruby manages memory usage using a garbage collector (also called gc).

Which method allocates memory for objects in Ruby?

Ruby releases a small amount of empty pages (a set of slots) at a time when there is too much memory allocated. The operating system call to malloc , which is currently used to allocate memory, may also release freed memory back to the operating system depending on the OS specific implementation of the malloc library.


1 Answers

When working with huge arrays of ActiveRecord objects be very careful... When processing those objects in a loop if on each iteration you are loading their related objects using ActiveRecord's has_many, belongs_to, etc. - the memory usage grows a lot because each object that belongs to an array grows...

The following technique helped us a lot (simplified example):

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 

In the code above "cloned_student" is the object that grows, but since it is "nullified" at the end of each iteration this is not a problem for huge array of students. If we didn't do "clone", the loop variable "student" would have grown, but since it belongs to an array - the memory used by it is never released as long as array object exists.

Different approach works too:

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 

In our production environment we had a background process that failed to finish once because 8Gb of RAM wasn't enough for it. After this small change it uses less than 1Gb to process the same amount of data...

like image 71
Alex Kovshovik Avatar answered Sep 20 '22 16:09

Alex Kovshovik