Do we need to close StringIO objects after usage in Ruby in order to free resources, like we do with the real IO objects?
obj = StringIO.new "some string"
#...
obj.close # <--- Do we need to close it?
Refining my question
Closing File object is necessary because it will close the file descriptor. The number of opened files is limited in the OS, and that’s why it is necessary to close File. But, if I understand correctly, StringIO is an abstraction in memory. Do we need to close it?
StringIO#closedoes not free any resources or drop its reference to the accumulated string. Therefore calling it has no effect upon resource usage.Only
StringIO#finalize, called during garbage collection, frees the reference to the accumulated string so that it can be freed (provided the caller does not retain its own reference to it).StringIO.open, which briefly creates a StringIO instances, does not keep a reference to that instance after it returns; therefore that StringIO’s reference to the accumulated string can be freed (provided the caller does not retain its own reference to it).In practical terms, there is seldom a need to worry about a memory leak when using StringIO. Just don’t hang on to references to StringIO once you’re done with them and all will be well.
Diving into the source
The only resource used by a StringIO instance is the string it is accumulating. You can see that in stringio.c (MRI 1.9.3); here we see the structure that holds a StringIO’s state:
When a StringIO instance is finalized (that is, garbage collected), its reference to the string is dropped so that the string may be garbage collected if there are no other references to it. Here’s the finalize method, which is also called by
StringIO#open(&block)in order to close the instance.The finalize method is called only when the object is garbage collected. There is no other method of StringIO which frees the string reference.
StringIO#closejust sets a flag. It does not free the reference to the accumulated string or in any other way affect resource usage:And lastly, when you call
StringIO#string, you get a reference to the exact same string that the StringIO instance has been accumulating:How to leak memory when using StringIO
All of this means that there is only one way for a StringIO instance to cause a resource leak: You must not close the StringIO object, and you must keep it around longer than you keep the string you got when you called
StringIO#string. For example, imagine a class having a StringIO object as an instance variable:Imagine that the user of this class gets the result, uses it briefly, and then discards it, and yet keeps a reference to the instance of Leaker. You can see that the Leaker instance retains a reference to the result via the un-closed StringIO instance. This could be a problem if the file is very large, or if there are many extant instance of Leaker. This simple (and deliberately pathological) example can be fixed by simply not keeping the StringIO as an instance variable. When you can (and you almost always can), it’s better to simply throw away the StringIO object than to go through the bother of closing it explicitly:
Add to all of this that these leaks only matter when the strings are large or the StringIO instances numerous and the StringIO instance is long lived, and you can see that explicitly closing StringIO is seldom, if ever, needed.