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?
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.
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.
Many modern programming languages manage memory for you, and Ruby is no different. Ruby manages memory usage using a garbage collector (also called gc).
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.
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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With