Ruby 程式設計/參考/物件/GC
Ruby 具有自動垃圾回收功能。
MRI 的 GC 是“完全標記和清除”,並在記憶體槽耗盡時執行(即在新增更多記憶體之前,它會掃描現有的記憶體,看看是否可以先釋放一些 - 如果不能,它會新增更多記憶體)。它也會在擴充套件分配 GC_MALLOC_LIMIT 位元組後觸發。不幸的是,這會導致對所有記憶體的遍歷,通常很慢。檢視 良好描述。
眾所周知,GC 通常會佔用 10% 的 CPU,但如果你的 RAM 負載很大,它可能會佔用更多。
GC 可以“編譯時”調整(MRI/KRI 的 < 1.9)http://blog.evanweaver.com/articles/2009/04/09/ruby-gc-tuning 或使用環境變數調整(REE、MRI/KRI 的 >= 1.9)。
一些提示
你可以將編譯器變數 GC_MALLOC_LIMIT 設定為一個非常高的值,這會導致你的程式使用更多 RAM,但更少地遍歷它。適用於大型應用程式,例如 rails。
你可以使用 jruby/rubinius,它們使用更復雜的 GC。
你可以使用“本地”庫,它們將值儲存起來,這樣 Ruby 就不用跟蹤它們並收集它們。示例:“NArray” gem 和“google_hash” gem。
要關閉它:@GC.disable@
要強制它執行一次:@GC.start@
Ruby(MRI)的 GC 是標記和清除,這意味著它是保守的。為了實現這一點,它會遍歷堆疊,查詢任何“看起來”像對現有 ruby 物件的引用的記憶體部分,並將它們標記為活動狀態。這會導致誤報,即使沒有對物件的引用剩餘。
這個問題在 1.8.x 系列中尤其嚴重,當他們沒有應用 MBARI 補丁時(大多數沒有,REE 有)。這是因為,當使用執行緒時,它實際上會為每個執行緒分配堆疊的完整副本,並且當執行緒執行時,它們的堆疊被複制到“真實”堆疊,它們可以拾取屬於其他執行緒的鬼引用,以及因為 1.8 MRI 直譯器包含巨大的 switch 語句,它們會留下大量未觸碰的堆疊記憶體,因此它可以繼續錯誤地包含對“鬼”引用的引用。
這意味著如果你呼叫 GC.start,它不能 *保證* 收集任何東西。
一些提示
- 如果你從方法內部呼叫你的程式碼,並在該方法 *退出*,它可能會更容易地收集它。
- 你可以使用 ensure 塊進行自己的 GC,例如
a = SomeClass.new begin ... ensure a.cleanup end
- 如果你編寫“更多”記憶體,它可能會清除堆疊上的舊引用。
"這裡":http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/27550 是一個調整 Jruby 的 GC 的示例。G1GC 理論上是一個“永不暫停”的 GC,儘管實際上大多數 GC 在速度方面都相當出色。對於長時間執行的應用程式,你可能需要在伺服器模式下執行(--server),以提高效能,儘管啟動時間會減少。
據說 Rubinius 也擁有更好的 GC。
由於 MRI 的 GC 本質上是 O(N),隨著它的增長,你在 GC 發生時會遇到效能損失,並且你的應用程式正在使用大量的 RAM(並且 MRI 幾乎從未將它的 RAM 返回系統)。解決方法
- 透過分配更少的物件來減少 RAM 使用量
- 在 fork 的“子程序”中執行工作,該子程序將返回所需的值。子程序將死亡,釋放其記憶體。
- 使用 Jruby 等(jruby 有一個優秀的 GC,即使在大型應用程式中也不會降低速度)。
- 使用允許使用本地型別的 gem,例如 NArray 或 RubyGoogle Hash。
- 使用 REE 而不是 1.8.6(因為它包含使 GC 更高效的 MBARI 補丁)。
- 使用 1.9.x 而不是 1.8.6(因為它使用真正的執行緒,堆疊上的鬼引用要少得多,因此使 GC 更高效)。
- 將你的應用程式設定為定期重啟(passenger 可以做到這一點)。
- 建立多個應用程式,一個設計為大型且緩慢,其他應用程式敏捷(將你的 GC 密集型操作全部執行在大型應用程式中)。
- 自己呼叫 GC.start,或將其與 GC.disable 混合使用
- 使用 memprof gem 檢視洩漏發生的位置(或 dike gem 或類似的 gem)。