Tips to avoid RMagick memory leaks

November 27, 2014, by Alexis Bernard

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:

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 !