2017-02-07 26 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