Rails内存泄漏:控制器类持有对实例的引用

时间:2017-02-07 21:34:21

标签: ruby-on-rails ruby actioncontroller

我在Rails 4.2.6应用程序中有内存泄漏。控制器将一个大的GaragesPresenter对象分配为一个实例变量,应该在请求完成后对其进行解引用和垃圾回收。但是,我发现这种情况从未发生过。

def show
  @garage = GaragesPresenter.new(@garage, view_context)
  respond_to do |format|
    format.html
  end
end

我看到GaragesPresenter实例正在引用GaragesController实例,而GaragesController类正在保存一个实例。在请求完成并且已调用GC.start之后很久就会这样。 为什么GaragesController类持有对实例的引用?

我知道这是因为我设置了一个堆转储:

require 'objspace'
...
GC.start
file = File.open("/tmp/dumpfile", 'w')
ObjectSpace.dump_all(output: file)

在结果文件中,我看到以下三个对象:

以下对象是GaragesPresenter,非常大:

{"address":"0x7fd077217e20", "type":"OBJECT", "class":"0x7fd074a04618", "ivars":7, "references":["0x7fd0772bf940", "0x7fd077711480", "0x7fd077748188", "0x7fd077772898", "0x7fd07720c778", "0x7fd0771ef8d0", "0x7fd0771ef8d0"], "file":"/Users/dyoung/workspace/commutyble/site-app/app/controllers/garages_controller.rb", "line":19, "method":"new", "generation":35, "memsize":56, "flags":{"wb_protected":true, "old":true, "marked":true}}

GaragesController实例正在保存对上述对象的引用(由于show方法将演示者分配为实例变量,因此预期:

{"address":"0x7fd0727559f0", "type":"OBJECT", "class":"0x7fd0727865a0", "ivars":22, "references":["0x7fd0727558b0", "0x7fd072755888", "0x7fd072755838", "0x7fd0732400e0", "0x7fd072754a50", "0x7fd0734c5658", "0x7fd07704e878", "0x7fd0732ab020", "0x7fd072785ee8", "0x7fd077217e20", "0x7fd0771ffe10", "0x7fd07720cde0", "0x7fd0732a82d0"], "file":"/Users/dyoung/.rvm/gems/ruby-2.1.0/gems/actionpack-4.2.6/lib/action_controller/metal.rb", "line":237, "method":"new", "generation":35, "memsize":176, "flags":{"wb_protected":true, "old":true, "marked":true}}

GaragesController类正在举行对上述GaragesController实例的引用,可能是为了防止garabage收集。为什么??

{"address":"0x7fd0727865a0", "type":"CLASS", "class":"0x7fd0726a7260", "name":"GaragesController", "references":["0x7fd0727559f0", "0x7fd0726a72b0"], "file":"/Users/dyoung/.rvm/gems/ruby-2.1.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb", "line":435, "method":"instance_exec", "generation":35, "memsize":672, "flags":{"wb_protected":true, "old":true, "marked":true}}

2 个答案:

答案 0 :(得分:3)

您需要使用WeakRef

  

弱引用类,允许引用的对象   垃圾回收。 WeakRef可以与它的对象完全一样使用   引用。

foo = Object.new

foo = WeakRef.new(foo) # Creates a weak reference to orig

ObjectSpace.garbage_collect

p foo.to_s       # should raise exception (recycled)

用例是使用两个对象引用的地方。首先是主人,第二是弱者。在使用第一个主链接之前,不会对您的对象进行垃圾回收。在对象中使用主链接(通用变量),它比我们需要引用对象的时间相同或更多。引用对象内部使用弱链接。

这种情况是相应的做法。在其他使用垃圾收集器的语言中,例如在perl中。 C ++库有太多的内存管理策略解决方案。垃圾收集器在使用时无法删除垃圾(对象)。如果对象引用另一个而另一个引用首先这意味着两者都在使用中。所以它不是垃圾 - 它对垃圾收集者的意见很有用#34;但实际上它是垃圾 - 它是内存泄漏。

对象引用图不能有循环或循环。如果我们需要在对象引用图中形成循环或循环的引用,我们需要在每个参考图中使用至少一个准切割弱引用。

答案 1 :(得分:3)

GaragesPresenter包含对view_context

的引用
@garage = GaragesPresenter.new(@garage, view_context)

view_context返回instance of of a view class,其中包含对self的引用,这是调用控制器:

# File actionview/lib/action_view/rendering.rb, line 71
def view_context
  view_context_class.new(view_renderer, view_assigns, self)
end