You've probably read somewhere that Symbols are never GC'ed in Ruby.
And if you've seen this you'll also know that a Symbol
takes at least around 60 bytes.
But did you keep that in mind the last time you were doing some meta-programming?
Very often, you need to come up with a unique name, and write things like
Then, you would often define a method with that name, as in
class Objectdef unsuspect(*args,&block)obj=selfclass <<obj;selfend.module_evaldoname="__unsuspect_#{rand(10000000)}_#{Thread.current.object_id.abs}"begindefine_method(name,&block)obj.send(name,*args)ensureremove_methodnameendendendend
Do you recognize that snippet? It's a simple Object#instance_exec implementation
(I've made a better one, but I'll write some more on this later):
"foo".unsuspect(" bar"){|x|self+x}# => "foo bar"
Now, did you know that the innocent looking #unsuspect method will bring your
long-running processes down to their knees?
When you do
define_method(name)
Ruby converts name into a Symbol. Which will never be released. So #unsuspect is leaking memory at the rate
of ~60 bytes (at least) per call. The amount of unreclaimed memory will be around 60 bytes times
the number of unique names that will be generated. In the above example, it'd be one million times the
total number of threads you will run over the life of the program
(not even simultaneously!)*1.
Some figures
Let's see a small example: if you perform 1000 calls to #unsuspect per thread, and create new threads
at the rate of 200 an hour, you'd be leaking as much as about