Ruby on Rails with imagemagick can eat up all memory

While developing a Ruby on Rails application, that has to do some image manipulation and make some database entries I had to make a weird experience that made me feel I was back in the good old C times.

Going through an array of pictures during development everything went fine. Performance was good and the pictures had a good quality.

Running in production mode the application became slower and slower. After some hundred pictures it took several minutes for every picture. My computer became unusable.

After a quick look I realized, that I had no byte of memory left and my swap partition was full. And full really means full! After killing the job my computer became usable again and I had a lot of free memory.

What was the reason?

Lets have a look at the code:

pix.each do |p|
      dst_dir = "#{prodpic_dir}#{out_path}#{p.filename[0,1]}/"
      src_filename = "#{prodpic_dir}#{in_path}#{p.filename[0,1]}/#{p.filename}"
      overlay_file = "#{prodpic_dir}#{overlay_path}#{article_efficiency[p.artNo.upcase]}.png"
      if ! Dir.exists? dst_dir
        Dir.mkdir dst_dir
      end
	
      if File.exists? src_filename
	
        src_file = Magick::Image.read(src_filename)[0]
        overlay_file = Magick::Image.read(overlay_file)[0]
        dst_file = "#{dst_dir}#{p.filename}"
        out_file = src_file.composite(overlay_file, Magick::EastGravity, 0, 0, Magick::OverCompositeOp)
        out_file.write(dst_file)
      end
    end

O.K. I am initializing the same variable several times, but the garbage collector should do its job and destroy the old object.

To make it short, the garbage collector does its job and destroys the ruby object, but it keeps the ImageMagick object until the complete task comes to an end. So after a while I had several hundred ImageMagick objects in my memory.

The only safe way to avoid that, is to destroy the image object before overwriting them again.

pix.each do |p|
      dst_dir = "#{prodpic_dir}#{out_path}#{p.filename[0,1]}/"
      src_filename = "#{prodpic_dir}#{in_path}#{p.filename[0,1]}/#{p.filename}"
      overlay_file = "#{prodpic_dir}#{overlay_path}#{article_efficiency[p.artNo.upcase]}.png"
      if ! Dir.exists? dst_dir
        Dir.mkdir dst_dir
      end
	
      if File.exists? src_filename
	
        src_file = Magick::Image.read(src_filename)[0]
        overlay_file = Magick::Image.read(overlay_file)[0]
        dst_file = "#{dst_dir}#{p.filename}"
        out_file = src_file.composite(overlay_file, Magick::EastGravity, 0, 0, Magick::OverCompositeOp)
        out_file.write(dst_file)
        src_file.destroy!
        overlay_file.destroy!
        out_file.destroy!
      end
    end
Back
Sascha Daniels - EDV Dienstleistungen - Frankfurter Str. 79 - 35440 Linden