临时文件和垃圾回收

3

我在一个Rails控制器中有这个命令

  open(source) { |s| content = s.read }
  rss = RSS::Parser.parse(content, false)

现在的问题是会产生临时文件,这些文件正在填满(稀缺的)磁盘空间。

我已经初步调查了这个问题,发现在某个地方出现了以下情况:

io = Tempfile.new('open-uri')

但是看起来这个Tempfile实例从未被明确关闭。它有一个

标签。
def _close  # :nodoc:

可能会在垃圾收集时自动触发的方法是什么?

了解正在发生的事情或如何清理临时文件的任何帮助都将非常有用。

3个回答

4

如果你真的想要强制 open-uri 不使用临时文件,你可以调整 OpenURI::Buffer::StringMax 常量:

> require 'open-uri'
=> true 
> OpenURI::Buffer::StringMax
=> 10240 
> open("http://www.yahoo.com")
=> #<File:/tmp/open-uri20110111-16395-8vco29-0> 
> OpenURI::Buffer::StringMax = 1_000_000_000_000
(irb):10: warning: already initialized constant StringMax
=> 1000000000000 
> open("http://www.yahoo.com")
=> #<StringIO:0x6f5b1c> 

这是因为在 open-uri.rb 中有以下代码片段:

class Buffer
  [...]
  StringMax = 10240
  def <<(str)
    [...]
    if [...] StringMax < @size
      require 'tempfile'

2
肯定不是一个错误,而是IO处理中的错误。如果@size小于10240个字节,则Buffer.io是StringIO;如果超过该值,则是Tempfile。在OpenURI.open_uri()中,ensure从调用close(),但因为它可能是StringIO对象,它没有close!()方法,所以不能仅仅调用close!()。
我认为解决方案应该是以下之一:
确保子句检查类并根据需要调用StringIO.close或Tempfile.close!。
-- 或 --
Buffer类需要一个终结器,它处理类检查并调用正确的方法。
当然,如果您不使用块来处理IO,那么这两种修复都无法解决问题,但我想在这种情况下,您可以自己进行检查,因为open()返回IO对象,而不是Buffer对象。
我的看法是,这个库是一大堆混乱的代码,所以它需要进行清理。我可能会这样做,只是为了好玩。 ^.^

SynTruth,我明白你的想法,但在一种鸭子类型的语言中,你不会检查类。你检查它是否响应一个方法 :)。无论如何,我现在离这个问题很远,如果你解决了它,请回报一下,只是出于好奇心。 - Dan Rosenstark

2
看起来_close关闭文件,然后等待垃圾回收来取消链接(删除)文件。理论上,您可以通过调用Tempfileclose!方法而不是close来立即强制取消链接,或者调用close(true)(内部调用close!)来立即强制取消链接。
编辑:但问题在于open-uri,这超出了您的控制范围-并且它不保证自我清理:它只是假设垃圾收集器会及时完成所有Tempfile的最终化。
在这种情况下,您别无选择,只能使用ObjectSpace.garbage_collect请参见此处)自己调用垃圾回收器。这应该导致所有临时文件的删除。

那么谁调用了_close函数?如果不使用猴子补丁,我就无法访问那个临时文件。 - Dan Rosenstark
我现在看到问题了。我在答案中添加了更多信息,希望对你有用。 - Guss
谢谢!我无法想象明确调用GC将解决问题。在我的测试中,使临时文件不清除的唯一方法是中断程序。所以我想不出可能发生了什么。 - Dan Rosenstark
我认为open-uri中存在一个错误(也许不是严格意义上的错误,但肯定是行为不当),它不能正确地清理临时文件。话虽如此——它在缓冲区之间传递得太频繁了,以至于很难确定何时关闭Tempfile。我相信通过大量重构可以解决open-uri中的问题,但这得等到另一个时间再说 :-) - Guss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接