2017-02-07 27 views
4

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

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实例(预计,随着显示方法分配呈现者作为一个实例变量):

{"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}}

+1

似乎'[WeakRef](红宝石-doc的。 org/stdlib-1.9.3/libdoc/weakref/rdoc/WeakRef.html)'可以帮助 – oklas

回答

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++库为内存管理策略提供了太多解决方案。垃圾收集器在使用时无法移除垃圾(物体)。如果对象引用另一个引用,则首先这意味着两个都在使用。所以它不是垃圾 - 它对“垃圾收集器的意见”很有用。但真的是垃圾 - 这是内存泄漏。

对象引用图必须不包含循环或循环。如果我们需要在对象引用图中形成循环或循环的引用,则我们需要在每个引用图中至少使用一个准切割弱引用。

+0

感谢您的提示。但我不希望我的'@ garage'对象被垃圾收集,直到我完成使用它。看起来如果我使用'WeakRef',那么在控制器方法完成之前就有可能收集垃圾。我错了吗? – davidgyoung

+0

总是会没事的。我做了一些改进的答案。在没有垃圾收集的地方使用主链接。 – oklas

+0

是的,这似乎可能与改善工作。但是这似乎和我一样,只是在我的方法结束时设置了@garage = nil。尽管其中任何一个都可能是一个合理的解决方法,但我的理解是,我不应该*需要*在Rails中执行此操作。我想先了解我做错了什么,如果有的话,这是导致这种意外的行为。 – davidgyoung

1

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 
+0

谢谢。这不应该阻止垃圾回收,对吧?我认为主持人可以持有对控制器实例的引用是可以的,因为两个对象只应在请求的整个生命周期内存在。请求结束后,这两个对象都应该有资格进行垃圾回收。 – davidgyoung

+0

它不应该,除非持有控制器或演示者的东西,在这种情况下,两者都将保持存在。我会仔细看看是否有任何常量发生了变异。 – fylooi

+0

是的,就我而言,我有证据表明某物正在持有对控制器的引用。 “是否有任何常量突变”是什么意思? – davidgyoung