簡體   English   中英

為什么 gen1/gen2 收集比 gen0 慢?

[英]Why are gen1/gen2 collections slower than gen0?

根據我的軼事知識,就 GC 而言,創建短期對象並不太麻煩——這意味着 gen0 集合非常快。 然而,Gen1/gen2 集合似乎更“可怕”,即據說通常比 gen0 慢很多。

這是為什么? 是什么讓 gen2 收集平均比 gen0 慢得多?

我不知道集合方法本身之間有任何結構差異(即在標記/清除/壓縮階段完成的事情),我是否遺漏了什么? 或者僅僅是因為例如 gen2 往往大於 gen0,因此需要檢查更多的對象?

為了放大 canton7 的回答,有必要注意一些額外的事情,其中​​之一增加了所有集合(尤其是 gen1 和 gen2)的成本,但降低了它們之間的分配成本,其中一個降低了 gen0 的成本和 gen1 集合:

  1. 許多垃圾收集器的行為有點類似於清理建築物,將所有有價值的東西轉移到另一棟建築物,炸毀原有建築物並重建空殼。 將事物從 gen0 建築物移動到 gen1 建築物的 gen0 集合將相當快,因為​​ gen0“建築物”中不會有太多東西。 一個 gen2 集合必須移動更大的 gen2 建築物中的所有東西。 垃圾收集系統可以為較小的 gen2 對象和較大的對象使用單獨的建築物,並通過跟蹤自由空間的各個區域來管理較大的建築物,但是移動較小的對象和回收批發存儲比嘗試管理所有單獨的存儲區域要少這將有資格重新使用。 然而,這里要觀察的一個關鍵點是,即使有必要掃描 gen1 或 gen2 對象,也沒有必要移動它,因為它所在的“建築物”不是立即拆除的目標。

  2. 許多系統使用“卡片表”,它可以記錄自上次 gen0 或 gen1 集合以來每個 4K 內存塊是否已被寫入,或包含用於修改對象的引用。 這會顯着減慢對任何此類存儲區域的首次寫入速度,但在 gen0 和 gen1 集合期間,可以跳過對大量對象的檢查。 卡片表的使用細節各不相同,但基本概念是,如果代碼有大量引用,但大部分都落在未標記的 4K 塊內,GC 甚至無需查看這些塊就可以知道那這將是通過訪問他們的任何新的對象將是其他方式訪問,因此將有可能找到所有GEN0對象,而不會打擾在所有這些塊的樣子。

請注意,即使沒有卡片表的簡單垃圾收集系統也可以簡單輕松地受益於分代 GC 的原理。 例如,在 Commodore 64 BASIC 上,它的垃圾收集器非常慢,一個創建了大量長壽命字符串的程序可以通過使用幾個 peek 和 poke 語句來調整字符串的頂部,從而避免冗長的垃圾收集周期——堆指針位於長期存在的字符串底部的正下方,因此它們不會被考慮用於重定位/回收。 如果一個程序使用數百個將在整個程序執行過程中持續存在的字符串(例如一個從 00 到 FF 的兩位十六進制字符串表),以及一些其他字符串,這可能會將垃圾收集時間減少一個數量級以上震級。

想到的幾個原因:

  1. 他們更大。 收集 gen1 意味着也收集 gen0,進行 gen2 收集意味着收集所有三個。 較低代的規模也較小,因為 gen0 收集最頻繁,因此需要便宜。
  2. 集合的主要成本是存活對象數量的函數,而不是死亡數量的函數。 分代垃圾收集器是圍繞分代假設構建的,該假設表示對象往往會存活很短的時間或很長時間,但不經常處於中間狀態。 根據其定義,Gen0 集合主要由在該代中消亡的對象組成,因此集合很便宜:gen1 和 gen2 集合具有更高比例的存活對象(理想情況下,gen2 應由存活的對象組成),因此更貴。
  3. 如果一個對象在 gen0 中,那么它只能被其他 gen0 對象引用,或者被更新為引用它的更高代中的對象引用。 因此,要查看 gen0 中的對象是否被引用,GC 需要檢查其他 gen0 對象,以及僅那些已更新為指向較低代對象的較高代對象(GC 跟蹤的對象,請參閱“卡片表” ”)。 要查看是否引用了 gen1 對象,它需要檢查所有 gen0 和 gen1,以及 gen2 中的更新對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM