如何在Ruby代码中追踪内存泄漏?

时间:2014-01-06 18:09:22

标签: ruby-on-rails ruby memory memory-leaks ruby-prof

问题

我正在调试rake任务中的内存泄漏。我想看一个调用堆栈:

  • 生活物品
  • 最初分配这些对象的对象或行

这可以用ruby-prof吗?

如果没有,我应该使用什么工具?

设置

宝石

佣金任务

  • 使用DATA LOAD INFILE和Active Record对象将CSV文件直接导入MySql数据库。

我尝试过什么

我尝试了模式

  • RubyProf ::拨款
  • RubyProf :: MEMORY

文档中的所有内容都是:

RubyProf ::拨款 对象分配报告显示程序中每个方法分配的对象数。

RubyProf :: MEMORY 内存使用情况报告显示程序中每个方法使用的内存量。

这意味着ruby-prof只报告对象的总分配,而不仅仅是生活对象的分配。

我尝试过Ruby-MassBloat Check,但似乎都无法做到我想要的。 Ruby-Mass也崩溃了,因为它出于某种原因在内存中找到了FactoryGirl对象......

4 个答案:

答案 0 :(得分:35)

我找不到ruby-prof在查找内存泄漏方面非常有用,因为你需要一个修补的Ruby解释器。在Ruby 2.1中,跟踪对象分配变得更加容易。也许这是自己探索的最佳选择。

我推荐tmml的博客文章Ruby 2.1: objspace.so,他是Ruby核心开发人员之一。基本上,您可以在调试应用程序时获取大量信息:

ObjectSpace.each_object{ |o| ... }
ObjectSpace.count_objects #=> {:TOTAL=>55298, :FREE=>10289, :T_OBJECT=>3371, ...}

require 'objspace'
ObjectSpace.memsize_of(o) #=> 0 /* additional bytes allocated by object */
ObjectSpace.count_tdata_objects #=> {Encoding=>100, Time=>87, RubyVM::Env=>17, ...}
ObjectSpace.count_nodes #=> {:NODE_SCOPE=>2, :NODE_BLOCK=>688, :NODE_IF=>9, ...}
ObjectSpace.reachable_objects_from(o) #=> [referenced, objects, ...]
ObjectSpace.reachable_objects_from_root #=> {"symbols"=>..., "global_tbl"=>...} /* in 2.1 */

使用Ruby 2.1,您甚至可以开始跟踪新对象的分配并收集有关每个新对象的元数据:

require 'objspace'
ObjectSpace.trace_object_allocations_start

class MyApp
  def perform
    "foobar"
  end
end

o = MyApp.new.perform
ObjectSpace.allocation_sourcefile(o) #=> "example.rb"
ObjectSpace.allocation_sourceline(o) #=> 6
ObjectSpace.allocation_generation(o) #=> 1
ObjectSpace.allocation_class_path(o) #=> "MyApp"
ObjectSpace.allocation_method_id(o)  #=> :perform

使用prypry-debugger并开始探索您认为可能会增长的内存堆,分别尝试代码中的不同段。在Ruby 2.1之前,我总是依赖于ObjectSpace.count_objects并计算结果的差异,以查看是否有一个对象类型特别增长。

当迭代期间生长的对象数量重新测试回到更小的量而不是继续增长时,垃圾收集正常工作。无论如何垃圾收集器应该一直运行,你可以通过查看Garbage Collector statistics.

来安抚自己

根据我的经验,这是字符串或符号(T_STRING)。符号before ruby 2.2.0未被垃圾收集,因此请确保您的CSV或部分内容未在途中转换为符号。

如果您感觉不舒服,请尝试使用JRuby在JVM上运行代码。至少使用VisualVM等工具可以更好地支持内存分析。

答案 1 :(得分:4)

考虑将the memory_profiler gem用于Ruby 2.1。

答案 2 :(得分:2)

有一个ruby-mass gem,它在ObjectSpace上提供了一个很好的api。

解决问题的方法之一是在完成对象后检查引用。

object = ...
# more logic
puts Mass.references(object)

如果至少有一个引用,则该对象不会被垃圾回收,您需要弄清楚如何删除该引用。例如:

object.instance_variable_set("@example", nil)

# or

ObjectSpace.each_object(Your::Object::Class::Name).each do |obj|
  obj.instance_variable_set("@example", nil)
end

答案 3 :(得分:2)

为了节省时间,您可以先查看有内存泄漏的Ruby gem列表。 https://github.com/ASoftCo/leaky-gems