Jas*_*abe 57 ruby valgrind memory-leaks
我发现我的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
Run Code Online (Sandbox Code Playgroud)
据说红宝石质量可以让我看到记忆中的所有物体.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
==================================================
Run Code Online (Sandbox Code Playgroud)
你可以看到内存去的方式了.一些计数器上升,但没有特定于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
Run Code Online (Sandbox Code Playgroud)
但同样,这并没有给我任何与CSSPool相关的信息,只有宝石信息等等.
我也尝试输出"GC.stat"......
puts GC.stat
CSSPool::CSS::Document.parse(File.new('/home/jason/big.css'))
ObjectSpace.garbage_collect
sleep 1
puts GC.stat
Run Code Online (Sandbox Code Playgroud)
结果:
{: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}
Run Code Online (Sandbox Code Playgroud)
据我了解,如果没有引用对象并且发生垃圾收集,那么应该从内存中清除该对象.但这似乎不是这里发生的事情.
我还读到了关于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
Run Code Online (Sandbox Code Playgroud)
结果在这里.我不确定这是否证实了C级漏洞,因为我还读到Ruby用Valgrind不理解的内存做事情.
使用的版本:
Ale*_*kin 39
看起来你正在这里进入失落的世界.我认为问题不在于c-bindings racc.
Ruby内存管理既优雅又繁琐.它将对象(名为RVALUEs)存储在大小约为16KB 的所谓堆中.在较低的层次上,RVALUE是一个c-struct,包含一个union不同的标准ruby对象表示.
因此,堆存储RVALUE对象,其大小不超过40个字节.对于这样的对象作为String,Array,Hash等等,这意味着小物体可以容纳在堆中,但一旦它们达到阈值时,红宝石堆之外的额外的存储器将被分配.
这种额外的记忆是灵活的; 一旦对象变成GC,它就会被释放.这就是为什么你的测试用例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
Run Code Online (Sandbox Code Playgroud)
但是,一旦被收购,堆(看GC[:heap_length])自己不会被释放回操作系统.看,我会对你的测试用例做一个单调的变化:
- big_var = " " * 10000000
+ big_var = 1_000_000.times.map(&:to_s)
Run Code Online (Sandbox Code Playgroud)
瞧,瞧:
# ? Memory 11788KB
# ? Memory 65188KB
# ? Memory 65188KB
# ? Memory 57448KB
Run Code Online (Sandbox Code Playgroud)
存储器不被释放回到OS了,因为该阵列的每个元素我介绍适合的RVALUE大小和被存储在红宝石堆.
如果您检查GC.statGC运行后的输出,您会发现该GC[:heap_used]值按预期减少.Ruby现在有很多空的堆,准备好了.
总结:我不认为,c代码泄漏.我认为问题在于你的巨大图像的base64表示css.我不知道解析器内部发生了什么,但看起来巨大的字符串强迫ruby堆计数增加.
希望能帮助到你.
Joe*_*gar 15
好的,我找到了答案.我正在离开我的另一个答案,因为这些信息很难收集,它是相关的,它可以帮助其他人搜索相关问题.
你的问题,但是,似乎是由于这样的事实,红宝石实际上并没有,一旦它已经收购了它释放内存返回到操作系统.
内存分配
虽然Ruby程序员不经常担心内存分配,但有时会出现以下问题:
为什么即使在我清除了对大对象的所有引用之后,我的Ruby进程仍然如此之大?我/确定/ GC已经运行了几次并释放了我的大对象而我没有泄漏内存.
AC程序员可能会问同样的问题:
我免费() - 记忆很多,为什么我的过程仍然那么大?
在大块中,来自内核的用户空间内存分配更便宜,因此用户空间通过自己做更多的工作来避免与内核的交互.
用户空间库/运行时实现内存分配器(例如:libc中的malloc(3)),它占用大块内核内存2并将它们分成更小的部分供用户空间应用程序使用.
因此,在用户空间需要向内核请求更多内存之前,可能会发生多个用户空间内存分配.因此,如果您从内核获得了大量内存并且只使用了一小部分内存,那么大块内存仍然会被分配.
将内存释放回内核也需要付出代价.用户空间内存分配器可以(私下)保留在该内存上,希望它可以在同一进程中重用,而不是将其返回给内核以便在其他进程中使用.(Ruby最佳实践)
因此,您的对象很可能已被垃圾收集并释放回Ruby的可用内存,但由于Ruby永远不会将未使用的内存返回给操作系统,因此即使在垃圾回收之后,该进程的rss值也保持不变.这实际上是设计的.根据Mike Perham的说法:
...由于MRI永远不会释放未使用的内存,因此我们的守护程序只需使用100-200即可轻松获取300-400MB.
重要的是要注意这基本上是设计的.Ruby的历史主要是作为文本处理的命令行工具,因此它重视快速启动和小内存占用.它不是为长时间运行的守护程序/服务器进程设计的.Java在其客户端和服务器VM中进行了类似的权衡.
这可能是由于Ruby 1.9.3及更高版本中的"Lazy Sweeping"功能.
懒惰扫描基本上意味着,在垃圾收集期间,Ruby只会"扫描"掉足够多的对象,以便为需要创建的新对象创建空间.这样做是因为,当Ruby垃圾收集器运行时,没有别的办法.这被称为"停止世界"垃圾收集.
从本质上讲,懒惰扫描减少了Ruby需要"阻止世界"的时间.你可以在这里阅读更多关于懒惰扫地的信息.
你的RUBY_GC_MALLOC_LIMIT环境变量是什么样的?
以下是Sam Saffron关于懒惰席卷的博客和RUBY_GC_MALLOC_LIMIT的摘录:
Ruby 2.0中的GC有两种不同的风格.我们有一个"完整"的GC,在我们分配了超过我们的malloc_limit和懒惰扫描(部分GC)之后运行,如果我们的堆中的空闲插槽耗尽将运行.
延迟扫描比完整GC花费的时间更少,但只执行部分GC.它的目标是更频繁地执行短GC,从而提高整体吞吐量.世界停止了,但时间更短.
malloc_limit设置为8MB开箱即用,你可以通过将RUBY_GC_MALLOC_LIMIT设置得更高来提高它.
你的RUBY_GC_MALLOC_LIMIT极高吗?我的设置为1亿(100MB).默认值大约为8MB,但对于rails应用程序,他们建议它要高一些.如果你的太高,可能会阻止Ruby删除垃圾对象,因为它认为它有足够的增长空间.
在@ mudasobwa的解释基础上,我终于找到了原因.CSSPool中的代码检查转义序列的非常长的数据URI.它将scan使用与转义序列或单个字符匹配的正则表达式调用URI,map这些结果将转换为unescape,然后join返回到字符串中.这实际上是为URI中的每个字符分配一个字符串.我将它修改为gsub转义序列,它似乎具有相同的结果(所有测试都通过)并大大减少了使用的结束内存.
使用与最初发布的相同的测试用例(减去Mass.print输出),这是更改之前的结果:
Memory 12404KB
Memory 292516KB
Run Code Online (Sandbox Code Playgroud)
这是改变后的结果:
Memory 12236KB
Memory 19584KB
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
13327 次 |
| 最近记录: |