31

问题

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

  • 有生命的物体
  • 最初分配这些对象的对象或行

ruby-prof 有可能吗?

如果不是,我应该使用什么工具?

设置

宝石

耙任务

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

我试过的

我已经尝试过这些模式

  • RubyProf::分配
  • RubyProf::MEMORY

它在文档中所说的只是:

RubyProf::ALLOCATIONS 对象分配报告显示程序中每个方法分配了多少对象。

RubyProf::MEMORY 内存使用报告显示程序中每个方法使用了多少内存。

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

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

4

4 回答 4

38

在定位内存泄漏时,我发现 ruby​​-prof 不是很有用,因为您需要一个修补过的 Ruby 解释器。在 Ruby 2.1 中跟踪对象分配变得更加容易。也许这是自己探索的最佳选择。

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

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-byebug并开始探索您认为可能会增长的内存堆,分别尝试代码中的不同段。在 Ruby 2.1 之前,我总是依赖ObjectSpace.count_objects并计算结果的差异,以查看一种对象类型是否特别增长。

当不断增长的对象数量在迭代过程中被重新测试到更小的数量时,垃圾收集可以正常工作,而不是继续增长。无论如何,垃圾收集器应该一直运行,您可以通过查看垃圾收集器统计信息来让自己放心。

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

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

于 2014-01-06T18:43:14.210 回答
5

考虑用于 Ruby 2.1的 memory_profiler gem 。

于 2014-11-10T15:41:27.317 回答
5

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

于 2015-10-23T16:13:28.223 回答
3

有一个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
于 2014-10-24T06:51:53.970 回答