簡體   English   中英

.NET CLR如何處理空類型的內存開銷?

[英]What does the .NET CLR do with the memory overhead in empty types?

所有.NET類都有作為其實例一部分存儲的同步塊和類型指針。 這些在32位進程中總共占用8個字節,在64位進程中總共占用16個字節。 但是,空類型實例的對象大小分別為12和24個字節。

我看過一些文章說這是一個對齊問題,但是由於同步塊和類型句柄是指針大小的,因此我不明白為什么需要添加任何填充。

其他文章說垃圾收集器需要它,但是它對開銷有什么作用? 它不能在那里存儲任何內容,因為如果類型具有實例字段,則實際上會使用額外的空間。 在對象完成之后,釋放之前,垃圾收集器是否需要使用該內存做某事(需要放一個東西(也許是一個指針))?

以下是一些我閱讀的有關空類型大小的文章:

.NET中類設計和通用編碼的性能考慮

如果您創建了一個沒有字段的對象,並在調試器中對其進行了查看,則會注意到它的大小實際上是12個字節,而不是8個字節。對於64位進程,該對象將是24個字節。 這是因為最小尺寸基於對齊。 幸運的是,這個“額外”的4個字節的空間將由一個字段使用。

內存和字符串

“最小”大小分別為12個字節和24個字節。 換句話說,您不能擁有僅屬於開銷的類型。 請注意,“ Empty”類如何占用與創建Object實例相同的大小……實際上有一些備用空間,因為CLR不喜歡對沒有數據的對象進行操作。

深入到.NET Framework內部,以查看CLR如何創建運行時對象

如前所述,當前的GC實現需要至少12個字節的對象實例。 如果一個類沒有定義任何實例字段,它將攜帶4個字節的開銷。 8個字節的其余部分將由對象頭(可能包含同步編號)和TypeHandle占用。

是的,即使是空類也需要4/8個字節用於對象的通常存儲對象字段的部分。 因此,一個完全空的類在32位模式下仍需要4 + 4 + 4 = 12個字節,在64位模式下則需要8 + 8 + 8 = 24個字節。 當對象存在時,根本就不會使用那些額外的4/8字節。

釋放對象時需要此存儲。 然后,它可以成為堆段的空閑塊列表的一部分。 如果堆段包含固定的對象並且無法完美壓縮,則會發生這種情況。 在這種情況下,在調試版本中將sync塊設置為-1,將類型句柄設置為內部偽造的FreeObject類型。 對象大小為4個字節。

SSCLI20源代碼可見,clr / src / vm / gcsmp.cpp文件,SetFree()函數。

啊,我知道發生了什么事。

數組將其長度存儲為實例字段數據的前4個或8個字節(分別在32位和64位系統中)(在類型指針之后)。 為了獲得內存使用量的大小,CLR從方法表中獲取基本大小(由類型指針指向),然后將長度乘以每個項目的大小(也從方法表中獲取)。

換句話說,公式為:

內存大小=基本大小+長度*項目大小

CLR的實現者不是對數組使用一個公式,而對其他類型使用一個不同的公式,而是希望對兩者都使用一個公式,因此在獲取內存大小時不需要任何條件邏輯。

但是那怎么工作呢? 其他對象類型不在其實例字段數據的前4或8個字節中存儲長度。

關鍵是項目大小,存儲在方法表中。 對於非數組類型,項目大小為0。這意味着length *項目大小將始終為0,而​​不管實例字段數據的前4或8個字節中存儲的值如何,該公式將起作用。

但是,即使實例字段數據的前4或8個字節的值無關緊要,仍需要對其進行分配以防止訪問沖突。

感謝Hans向我指出SetFree方法。 當我看到CLR本質上將一個任意對象轉換為一維字節數組時,我意識到這是假定一切都可以以這種方式轉換的原因,而對原因的調查使我想到了這個答案。

暫無
暫無

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

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