One commenter (my pal Aleks) said to ‘watch out for RMagick as it has major memory leaks. I talked to him further and he recommended using MiniMagick instead. I investigated.
It turns out indeed that my RMagick app was gobbling up tons of memory.
- RMagick uses the ImageMagick API and presents a comprehensive ‘rubyfication’ of the ImageMagtick api. This is useful but you do find that you jump back and forth between the RMagick doc and the ImageMagick doc to see what methods in one correspond to which ones in the other. MiniMagick on the other hand is a very thin veneer over ImageMagick‘s command line utilities. It generally uses ‘method-not-found’ to decide to invoke the corresponding C method. This means that the ImageMagick doc is your primary source. The MiniMagick source itself is a single tiny (but sophisticated) Ruby script.
- RMagick creates in memory ImageMagick (‘malloc‘) objects and retains pointers to those inside of Ruby structures. Unless those Ruby structures are garbage collected, the ImageMagick objects just hang around and eat memory, hence the memory leak reputation. Because as far as Ruby is concerned, not that much memory has been used yet, natural garbage collections don’t occur and your system memory footprint gets bigger and bigger.
- MiniMagick works only with files; it never creates the ImageMagick malloc objects and hence does not suffer from the memory leak. On the other hand, with a complex process like what I am working on, you are creating, saving and reading a lot of files, which slows things down.
I did nearly a day of coding comparing the two and while MiniMagick made the memory leaks go away, in the end it was slower for my purposes than RMagick, probably due to the files that were constantly being created, opened and saved. Note after a few dozen complicated operations, the RMagick version got super slow and basically hung because of memory consumption.
That was easily solved with a little research. I found a post that explained how to address the apparent memory leaks in RMagick: I added a forced reclamation of the ImageMagick malloc object whenever I am done with one (Image.destroy! is the call) and the huge leak is gone. Not sure yet whether there are other ones, but for now, RMagick wins for me!