我在我的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
==================================================
你可以看到内存急剧增加,一些计数器上升,但没有特定于CSSPool的对象出现。我使用ruby-mass的“index”方法来检查具有参考引用的对象,如下所示:
Mass.index.each do |k,v|
v.each do |id|
refs = Mass.references(Mass[id])
puts refs if !refs.empty?
end
end
但是,这并没有给我任何与CSSPool相关的内容,只有宝石信息之类的东西。
我还尝试了输出“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}
据我理解,如果一个对象没有被引用且垃圾回收发生了,那么该对象应该从内存中清除。但这似乎不是这里所发生的情况。
我也读过关于C级内存泄漏的文章,并且由于CSSPool使用Racc,而Racc使用C代码,因此我认为这是一个可能性。我已经通过Valgrind对我的代码进行了运行:
valgrind --partial-loads-ok=yes --undef-value-errors=no --leak-check=full --fullpath-after= ruby leak.rb 2> valgrind.txt
结果在这里。我不确定这是否确认了 C 级泄漏,因为我也读到 Ruby 对内存的处理方式 Valgrind 无法理解。
使用的版本:
- Ruby 2.0.0-p247(我的 Rails 应用程序运行的版本)
- Ruby 1.9.3-p392-ref(用于使用 ruby-mass 进行测试)
- ruby-mass 0.1.3
- CSSPool 4.0.0从这里下载
- CentOS 6.4 和 Ubuntu 13.10