怎么在Ruby中查找内存泄漏的原因?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (87)

我在Rails代码中发现了内存泄漏 - 也就是说,我发现了有代码泄漏,但没有发现泄漏的原因。我将它缩减为一个不需要Rails的测试用例:

require 'csspool'
require 'ruby-mass'

def report
    puts 'Memory ' + `ps ax -o pid,rss | grep -E "^[[:space:]]*#{$$}"`.strip.split.map(&:to_i)[1].to_s + 'KB'
    Mass.print
end

report

# note I do not store the return value here
CSSPool::CSS::Document.parse(File.new('/home/jason/big.css'))

ObjectSpace.garbage_collect
sleep 1

report

据说ruby-mass能看到内存中的所有对象。CSSPool是一个基于racc的CSS解析器。/home/jason/big.css是一个1.5MB的CSS文件

这输出:

Memory 9264KB

==================================================
 Objects within [] namespace
==================================================
  String: 7261
  RubyVM::InstructionSequence: 1151
  Array: 562
  Class: 313
  Regexp: 181
  Proc: 111
  Encoding: 99
  Gem::StubSpecification: 66
  Gem::StubSpecification::StubLine: 60
  Gem::Version: 60
  Module: 31
  Hash: 29
  Gem::Requirement: 25
  RubyVM::Env: 11
  Gem::Specification: 8
  Float: 7
  Gem::Dependency: 7
  Range: 4
  Bignum: 3
  IO: 3
  Mutex: 3
  Time: 3
  Object: 2
  ARGF.class: 1
  Binding: 1
  Complex: 1
  Data: 1
  Gem::PathSupport: 1
  IOError: 1
  MatchData: 1
  Monitor: 1
  NoMemoryError: 1
  Process::Status: 1
  Random: 1
  RubyVM: 1
  SystemStackError: 1
  Thread: 1
  ThreadGroup: 1
  fatal: 1
==================================================

Memory 258860KB

==================================================
 Objects within [] namespace
==================================================
  String: 7456
  RubyVM::InstructionSequence: 1151
  Array: 564
  Class: 313
  Regexp: 181
  Proc: 113
  Encoding: 99
  Gem::StubSpecification: 66
  Gem::StubSpecification::StubLine: 60
  Gem::Version: 60
  Module: 31
  Hash: 30
  Gem::Requirement: 25
  RubyVM::Env: 13
  Gem::Specification: 8
  Float: 7
  Gem::Dependency: 7
  Range: 4
  Bignum: 3
  IO: 3
  Mutex: 3
  Time: 3
  Object: 2
  ARGF.class: 1
  Binding: 1
  Complex: 1
  Data: 1
  Gem::PathSupport: 1
  IOError: 1
  MatchData: 1
  Monitor: 1
  NoMemoryError: 1
  Process::Status: 1
  Random: 1
  RubyVM: 1
  SystemStackError: 1
  Thread: 1
  ThreadGroup: 1
  fatal: 1
==================================================

我使用ruby-mass的“索引”方法来检查具有引用的对象,如下所示:

Mass.index.each do |k,v|
    v.each do |id|
        refs = Mass.references(Mass[id])
        puts refs if !refs.empty?
    end
end

但是,这不会给我任何与CSSPool相关的东西,只是gem信息。

我也尝试输出“GC.stat”...

puts GC.stat
CSSPool::CSS::Document.parse(File.new('/home/jason/big.css'))
ObjectSpace.garbage_collect
sleep 1
puts GC.stat

结果:

{:count=>4, :heap_used=>126, :heap_length=>138, :heap_increment=>12, :heap_live_num=>50924, :heap_free_num=>24595, :heap_final_num=>0, :total_allocated_object=>86030, :total_freed_object=>35106}
{:count=>16, :heap_used=>6039, :heap_length=>12933, :heap_increment=>3841, :heap_live_num=>13369, :heap_free_num=>2443302, :heap_final_num=>0, :total_allocated_object=>3771675, :total_freed_object=>3758306}

据我了解,如果一个对象没有被引用并且垃圾收集发生,那么该对象应该从内存中清除。但这似乎并不是这里发生的事情。

  • Ruby 2.0.0-p247
  • Ruby 1.9.3-p392-ref
  • rubymass 0.1.3
  • CSSPool 4.0.0
  • CentOS 6.4和Ubuntu 13.10
提问于
用户回答回答于

Ruby内存管理既优雅又繁琐。它将对象(名为RVALUEs)存储在大小约为16KB 的所谓堆中。在低层次上,RVALUE是一个c-struct,包含了union不同的标准ruby对象表示。

所以,堆RVALUE对象的大小不超过40字节。对于这样的对象作为StringArrayHash等等,这意味着小物体可以容纳在堆中,但一旦它们达到阈值时,堆之外的额外的存储器将被分配。

这个额外的内存是灵活的; 只要一个对象变成GC'ed就会被释放。这就是为什么你的测试用例big_string显示内存上下行为:

def report
  puts 'Memory ' + `ps ax -o pid,rss | grep -E "^[[:space:]]*#{$$}"`
          .strip.split.map(&:to_i)[1].to_s + 'KB'
end
report
big_var = " " * 10000000
report
big_var = nil 
report
ObjectSpace.garbage_collect
sleep 1
report
# ⇒ Memory 11788KB
# ⇒ Memory 65188KB
# ⇒ Memory 65188KB
# ⇒ Memory 11788KB

但是堆积(see GC[:heap_length])本身并没有被释放回操作系统。我会对你的测试用例做一个单调的改变:

- big_var = " " * 10000000
+ big_var = 1_000_000.times.map(&:to_s)

并且:

# ⇒ Memory 11788KB
# ⇒ Memory 65188KB
# ⇒ Memory 65188KB
# ⇒ Memory 57448KB

存储器不被释放回到OS。

用户回答回答于

好的,我找到了答案。我正在离开我的另一个答案,因为这些信息很难收集,是相关的,它可以帮助别人寻找相关问题。

但是,问题似乎是由于Ruby实际上一旦获得内存就不会将内存释放回操作系统。

扫码关注云+社区