Tips to avoid RMagick memory leaks
RMagick has a bad reputation because of memory leaks. I had memory leaks issue while working on DISCO.IO where Ruby processes kept bloating. That issue was really annyoing since millions of pictures are resized. Finally I succeed to fix my code. Now memory usage is stable. Here is how I did it.
TL;DR for hurry people:
- Call Magick::Image#destroy! (ensure block is a damn good idea)
- Use methods with exclamation mark as much as spossible
Here is the code before improvements:
def resize_picture(data, size)
picture = Magick::Image.from_blob(data).first
if (min_size = [picture.rows, picture.columns].min) >= 360
picture = picture.crop(Magick::CenterGravity, min_size, min_size)
end
picture = picture.resize_to_fill(size, size) if picture.rows > size
result = normalize_picture(picture)
picture.destroy!
result
end
Here is the code after fixing memory leaks with changes in bold:
def resize_picture(data, size)
picture = Magick::Image.from_blob(data).first
if (min_size = [picture.rows, picture.columns].min) >= 360
picture.crop!(Magick::CenterGravity, min_size, min_size)
end
picture.resize_to_fill!(size, size) if picture.rows > size
normalize_picture(picture)
ensure
picture && picture.destroy!
end
Calling the crop! instead of crop is a huge memory save, because the latter creates a copy the of the picture. Like the Ruby convention of Enumerable, Array and Hash, RMagick provides the 2 versions for all methods.
Finally destroy! is called from an ensure block to free memory in case of any error.
That is not really complicated and makes me really happy to stop Ruby processes from eating all the memory. I hope that will work for you too !