I'm using RMagick to do some fairly simple image manipulations in a Rails 3 app (running on Passenger). I'm having a terrible experience trying to control associated memory leaks, despite using the destroy! method. An important thing to note is that the incoming images may be transparent PNGs, and I want them on a white background, not the default black which RMagick uses. Others may be PDFs, in which case I want to use the first page.
Currently resizing a 3.6MB JPEG results in a 110MB memory leak.
So, is anybody able to take a look at my sample and maybe spot where I'm going wrong:
path = 'some/path'
page_id = "0"
in_doc_o = nil
begin
in_doc_o = ImageList.new(path+"[#{page_id}]")
# Create a white background layer
in_doc_temp = in_doc_o.new_image(in_doc_o.first.columns, in_doc_o.first.rows) { self.background_color = "white" }
in_doc = in_doc_o.reverse.flatten_images
in_doc.format = "jpg"
rescue
# Something went wrong so create a dummy 'error' image
in_doc = ImageList.new("public/images/doc.jpg"+"[#{page_id}]")
end
in_doc.change_geometry!('600x200>') { |cols, rows, img| img.resize!(cols, rows) }
out_blob = in_doc.to_blob() { self.quality = 60 }
res = save_to_special_storage_device(out_blob)
mime_type = in_doc.mime_type
begin
in_doc.each {|img| img.destroy!}
rescue=>e
Rails.logger.info "rescued attempting destroy on in_doc image list #{e.inspect}"
end
begin
in_doc_o.each {|img| img.destroy!}
rescue=>e
Rails.logger.info "rescued attempting destroy on in_doc_o image list #{e.inspect}"
end
begin
in_doc.destroy!
in_doc_o.destroy! unless in_doc_o.nil?
in_doc = nil
in_doc_o = nil
in_doc_temp.destroy! if in_doc_temp
in_doc_temp = nil
rescue
Rails.logger.info "rescued attempting image cleanups"
end
GC.start
I'm guessing that I could probably save some issues at the expense of a little more code by identifying PNGs up front and not creating the blank white image, but in tests I still leak 60MB which suggests a fundamental issue that I would like to resolve.
Strangely, or not, if I re-run the test with another image of approximately the same size, the memory usage does not jump significantly a second time (fortunately).
Any reasonable ideas would be appreciated, especially with a view of how much memory I could save.
Try minimagic, which was written to deal with exactly the problem you're having.
For most tasks, the minimagic gem linked here is sufficient. It runs ImageMagick's command-line tools, which means a new process is created, it runs, and then stops, freeing memory. We replaced RMagic with MiniMagic and our memory woes were a thing of the past.