0

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.

Phil
  • 2,797
  • 1
  • 24
  • 30

1 Answers1

0

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.

Tom Harrison
  • 13,533
  • 3
  • 49
  • 77
  • Hmm, really hoping I can avoid re-coding everything. Though it does seem that I might have to. I'm going to hold the question open for a little longer, just in case I get an RMagick-specific solution. – Phil Nov 20 '12 at 22:41
  • Read the rationale the author of the gem describes for writing it. It's a pig when run in-process, and leaks memory badly. – Tom Harrison Nov 21 '12 at 01:33
  • Yep, read the rationale previously. I guess I was holding out hope for avoiding a complete rewrite to use a new gem, and somebody had found a magic bullet to the memory leak issues. Oh well, thanks for your input. – Phil Nov 26 '12 at 15:18