【问题标题】:How to prevent memory leaks in RubyMotion when reading large files in loops循环读取大文件时如何防止RubyMotion中的内存泄漏
【发布时间】:2016-05-09 15:31:15
【问题描述】:

RubyMotion 应该做自动内存管理:

RubyMotion 提供自动内存管理;你不需要 回收未使用的对象。

但是,在循环读取大文件时,我遇到了巨大的内存泄漏:每秒数百 MB/s,就像我的读取缓冲区从未释放一样。

如果我在每个循环的读取缓冲区上使用release,泄漏大多会消失。问题是,release 在循环结束时使应用程序崩溃。

  def readBigBinaryFile(file)
#   PURE RUBY WOULD BE
#   source=File.open(file,'r')
    source =NSFileHandle.fileHandleForReadingAtPath(file)
    buffer_size=4096
    offset=0
    size=File.size(file)
    while ( offset + buffer_size ) <= size
#      PURE RUBY WOULD BE
#      source.seek(offset) 
#      abuffer = source.read( buffer_size )
#      abuffer=abuffer.to_s

      source.seekToFileOffset(offset)
      abuffer = source.readDataOfLength(buffer_size)
      offset+=buffer_size
      @dataPointer ||= Pointer.new(:uchar,4) 
      abuffer.getBytes(@dataPointer, length: 4)
      # memory leaks very rapidly in GBs if we don't release the buffer…
      # but this relase action will make the application crash once the doSomething lookp is finished
      abuffer.release
    end
    source.closeFile
    return false
  end

循环是:

x=0
while x < 10000
  my_scan_binary_instance=Scan_binary.new()        result=my_scan_binary_instance.readBigBinaryFile(NSBundle.mainBundle.pathForResource("sample1MBfile", ofType:"img"))
  puts result.to_s
  x+=1
end
puts "if we have used 'abuffer.release', we are going to crash now"

我测试了一个纯 Ruby 实现,完全没有内存泄漏,也不需要 release 调用。

我发现“How do I prevent memory leak when I load large pickle files in a for loop?”关于 Python 循环中的内存泄漏,但在 readBigBinaryFile 中的 while block 开头执行 abuffer=nil 的公认解决方案不起作用。

这是 RubyMotion 的自动内存管理中的错误,还是预期的? 最重要的是,如何在不增加 RubyMotion 应用程序内存使用量的情况下循环读取大文件?

我已经创建了 a gist 与工作的纯 Ruby 实现,以及重现崩溃的示例应用程序的 a repo

【问题讨论】:

  • 我建议阅读“How to Ask”,尤其是指向catb.org/esr/faqs/smart-questions.html 的链接。 “minimal reproducible example”指定您的示例应用程序必须在问题本身中。指向其他网站的链接很容易腐烂然后中断,从而使将来的问题对其他人毫无价值。
  • 外部链接是一种商品,希望问题本身嵌入代码,使问题变得完整,并且现在和以后对其他人有用

标签: ruby cocoa memory-management memory-leaks rubymotion


【解决方案1】:

尝试将循环体包裹在 autorelease_pool do ... end 中。这应该导致 每次循环释放的自动释放对象。在循环结束时将nil 分配给abuffer 将允许释放缓冲内存,因为将不再有对它的引用。

while ( offset + buffer_size ) <= size
  autorelease_pool do
    source.seekToFileOffset(offset)
    abuffer = source.readDataOfLength(buffer_size)
    offset+=buffer_size
    @dataPointer ||= Pointer.new(:uchar,4) 
    abuffer.getBytes(@dataPointer, length: 4)

    abuffer = nil
  end
end

【讨论】:

  • 效果很好!如果我还在my_scan_binary_instance=Scan_binary.new() 周围放置一个自动释放,内存泄漏会更少。 Link to Apple docs
【解决方案2】:

我不知道这是否会解决它,但也许可以尝试更换

abuffer = source.readDataOfLength(buffer_size)

abuffer = WeakRef.new(source.readDataOfLength(buffer_size))

有关WeakRef 的信息,请参阅“4.1. Weak References”。

进行此更改意味着您还将删除对 abuffer.release 的调用。

【讨论】:

  • 好主意!但它不起作用。因此,我尝试将 WeakRefs 放在任何我能做到的地方,并在 doSomething 的 while 循环中添加一个 .weak!... 无济于事!我还阅读了spin.atomicobject.com/2013/12/09/cyclical-references-rubymotion
  • 欢迎来到 SO。请为链接使用更好的锚文本,并避免使用“编辑”或“更新”。问题和答案具有修订历史记录,因此我们可以在必要时查看更改;添加更改,就像您最初编写它们一样。更好的锚文本有助于保持内容的可读性。在您的答案中总结该链接处的信息也是一个非常好的主意,因此如果链接失效,那么有用的信息仍然可供未来的搜索者使用。
  • @MichaelC 我最好的建议是在RubyMotion forums 上提问,在那里您可能会更好地了解您的问题。
猜你喜欢
  • 1970-01-01
  • 2013-04-23
  • 2014-05-17
  • 1970-01-01
  • 2012-11-19
  • 2010-12-20
  • 1970-01-01
相关资源
最近更新 更多