我在Rails代码中发现了内存泄漏——也就是说,我发现了what个代码泄漏,但没有发现why个.我将其简化为一个不需要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解析器/家/杰森/大.css是a 1.5MB CSS file.

这将产生:

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
==================================================

你可以看到记忆上升了way.一些计数器上升,但不存在特定于CSSPool的对象.我使用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}

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

我也读过关于C级内存泄漏的文章,由于CSSPool使用的是使用C代码的Racc,我认为这是一种可能性.我已经通过Valgrind运行了我的代码:

valgrind --partial-loads-ok=yes --undef-value-errors=no --leak-check=full --fullpath-after= ruby leak.rb 2> valgrind.txt

结果是here.我不确定这是否证实了C级泄漏,因为我还读到Ruby使用Valgrind不理解的内存进行操作.

使用的版本:

  • Ruby 2.0.0-p247(我的Rails应用程序就是这样运行的)
  • Ruby 1.9.3-p392-ref(用于使用Ruby mass进行测试)
  • Ruby 质量0.1.3
  • CS4.0.0从here开始
  • CentOS 6.4和Ubuntu 13.10

推荐答案

看起来你要进入The Lost World了.我认为问题也不在于racc中的c绑定.

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

因此,堆存储RVALUE个对象,大小不超过40字节.对于StringArrayHash等对象,这意味着小对象可以放入堆中,但一旦它们达到阈值,就会分配Ruby堆之外的额外内存.

This extra memory is flexible; is will be freed as soon as an object became 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

但一旦获得,堆(见GC[:heap_length])本身就可以返回操作系统.听着,我会对你的测试用例做一个简单的修改:

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

瞧́

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

内存不再释放回操作系统,因为我在ruby堆中引入了suitsis stored的数组元素.

如果在GC运行后判断GC.stat的输出,会发现GC[:heap_used]的值按预期减少.Ruby现在有很多空堆,准备好了.

The summing up:我不认为,c码泄露了.我认为问题在于你的css中巨大图像的base64表示.我不知道解析器内部发生了什么,但看起来这个巨大的字符串迫使ruby堆计数增加.

希望有帮助.

Ruby相关问答推荐

VS Code Prettier 打破哈希访问

令人惊讶的有效 Ruby 语法:% 无处不在

Ruby ERB 类给出未定义的局部变量或方法,但 erb 解析良好

使用正则表达式判断用户输入的开头是否正好有两个大括号

ruby:rubocop 抱怨将 if else 转换为 case 语句

如何在 OpenURI 中指定http 请求标头

什么是 '?-mix' 在 Ruby 正则表达式中

以编程方式获得完整的 Ruby 版本?

如何按长度对 Ruby 字符串数组进行排序?

RSpec 是否有可能期望在两个表中发生变化?

您如何将 Cucumber 场景标记为待处理

将元素添加到 ruby​​ 数组返回新数组

class_eval、class_exec、module_eval 和 module_exec 有什么区别?

Ruby 中方法名称的限制是什么?

线程安全:Ruby 中的类变量

为什么在 ruby​​ / rails / activerecord 中并不总是需要 self ?

铲子 (<<) 运算符如何在 Ruby 哈希中工作?

如何在器上下文中运行 ruby​​ 脚本?

`gem install therubyracer` 在 Mac OS X Lion 上失败

检索/列出 Redis 数据库中的所有键/值对