Is it necessary to close StringIO in ruby?

后端 未结 5 682
眼角桃花
眼角桃花 2021-01-01 20:00

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\"
         


        
相关标签:
5条回答
  • 2021-01-01 20:06

    In response to the other answers here: calling close won't help you save memory.

    require "stringio"
    sio = StringIO.new
    sio.print("A really long string")
    sio.close
    sio.string # => "A really long string"
    

    "A really long string" will stay around as long as sio does, close or no close.

    So why does StringIO have a close method? Duck typing. Providing close, and throwing an IOError if you try to read or write from it, ensures it acts like a real File object would. This is useful if you're using it as a mock object while unit testing.

    0 讨论(0)
  • 2021-01-01 20:10

    When you close a File, it is important as the system has a limited amount of descriptors (assuming your on a UNIX, I have no clue what Windows does). With StringIO, there is another resource at stake: Memory. If the StringIO object has a lot to store, there is going to be a lot of memory claimed from the heap. On the other hand, the garbage collector does always close IO objects that it claims, but that assumes that you have an elegant way to put your object out of scope. Also, on a heavily loaded system, physical RAM is much more valuable then file descriptors. On my Linux box, 178203 is the max file descriptors. I doubt that you could reach that.

    0 讨论(0)
  • 2021-01-01 20:13

    No, but it is good for memory optimization

    0 讨论(0)
  • 2021-01-01 20:14
    • StringIO#close does 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:

    static struct StringIO *struct StringIO {
        VALUE string;
        long pos;
        long lineno;
        int flags;
        int count;
    };
    

    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.

    static VALUE
    strio_finalize(VALUE self)
    {
        struct StringIO *ptr = StringIO(self);
        ptr->string = Qnil;
        ptr->flags &= ~FMODE_READWRITE;
        return self;
    }
    

    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#close just sets a flag. It does not free the reference to the accumulated string or in any other way affect resource usage:

    static VALUE
    strio_close(VALUE self)
    {   
        struct StringIO *ptr = StringIO(self);
        if (CLOSED(ptr)) {
            rb_raise(rb_eIOError, "closed stream");
        }
        ptr->flags &= ~FMODE_READWRITE;
        return Qnil;
    }
    

    And lastly, when you call StringIO#string, you get a reference to the exact same string that the StringIO instance has been accumulating:

    static VALUE
    strio_get_string(VALUE self)
    {   
        return StringIO(self)->string;
    }
    

    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:

    class Leaker
    
      def initialize
        @sio = StringIO.new
        @sio.puts "Here's a large file:"
        @sio.puts
        @sio.write File.read('/path/to/a/very/big/file')
      end
    
      def result
        @sio.string
      end
    
    end
    

    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:

    class NotALeaker
    
      attr_reader :result
    
      def initialize
        sio = StringIO.new
        sio.puts "Here's a large file:"
        sio.puts
        sio.write File.read('/path/to/a/very/big/file')
        @result = sio.string
      end
    
    end
    

    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.

    0 讨论(0)
  • 2021-01-01 20:15

    In general, the answer is no. I/O streams are automatically closed when claimed by the garbage collector. Same answer to File I/O as well.

    0 讨论(0)
提交回复
热议问题