簡體   English   中英

.net托管內存如何處理對象內的值類型?

[英]How does .net managed memory handle value types inside objects?

public class MyClass
{
    public int Age;
    public int ID;
}

public void MyMethod() 
{
    MyClass m = new MyClass();
    int newID;
}

據我了解,以下是真實的:

  1. 當MyMethod()退出時,引用m存在於堆棧中並超出范圍。
  2. 值類型newID存在於堆棧中,並在MyMethod()退出時超出范圍。
  3. 當MyMethod()退出時,由new運算符創建的對象存在於堆中並由GC回收,假設不存在對該對象的其他引用。

這是我的問題:

  1. 對象中的值類型是否存在於堆棧或堆上?
  2. 對象中的裝箱/取消裝箱值類型是否值得關注?
  3. 關於這個主題,是否有任何詳細但可理解的資源?

從邏輯上講,我認為類中的值類型會在堆中,但我不確定是否必須將它們裝入盒中。

編輯:

建議閱讀本主題:

  1. CLR通過C#by Jeffrey Richter
  2. Don Box的Essential .NET

類的值類型值必須與托管堆中的對象實例一起存在。 方法的線程堆棧僅在方法的持續時間內存在; 如果該值僅存在於該堆棧中,該值如何保持不變?

托管堆中的類對象大小是其值類型字段,引用類型指針和其他CLR開銷變量(如同步塊索引)的總和。 當一個值為對象的value-type字段賦值時,CLR將該值復制到該特定元素字段的對象內分配的空間。

舉個例子,一個帶有單個字段的簡單類。

public class EmbeddedValues
{
  public int NumberField;
}

有了它,一個簡單的測試類。

public class EmbeddedTest
{
  public void TestEmbeddedValues()
  {
    EmbeddedValues valueContainer = new EmbeddedValues();

    valueContainer.NumberField = 20;
    int publicField = valueContainer.NumberField;
  }
}

如果您使用.NET Framework SDK提供的MSIL反匯編程序來查看EmbeddedTest.TestEmbeddedValues()的IL代碼

.method public hidebysig instance void  TestEmbeddedValues() cil managed
{
  // Code size       23 (0x17)
  .maxstack  2
  .locals init ([0] class soapextensions.EmbeddedValues valueContainer,
           [1] int32 publicField)
  IL_0000:  nop
  IL_0001:  newobj     instance void soapextensions.EmbeddedValues::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4.s   20
  IL_000a:  stfld      int32 soapextensions.EmbeddedValues::NumberField
  IL_000f:  ldloc.0
  IL_0010:  ldfld      int32 soapextensions.EmbeddedValues::NumberField
  IL_0015:  stloc.1
  IL_0016:  ret
} // end of method EmbeddedTest::TestEmbeddedValues

請注意,CLR被告知將堆棧中加載的值“20” stfld加載到加載的EmbeddValues的NumberField字段位置,直接進入托管堆。 類似地,在檢索值時,它使用ldfld指令直接將該托管堆位置的值復制到線程堆棧中。 這些類型的操作不會發生框/拆箱。

  1. 對象在堆中擁有的任何引用或值類型。
  2. 只有當你向對象施放內容時。

我見過的最好的資源是傑弗里里希特的書籍CLR by C#。 如果您進行任何.NET開發,那么非常值得一讀。 基於該文本,我的理解是引用類型中的值類型確實存在於父對象中嵌入的堆中。 引用類型始終在堆上。 拳擊和拆箱不對稱。 拳擊比拆箱更重要。 拳擊需要將值類型的內容從堆棧復制到堆。 根據這種情況發生的頻率,有一個結構而不是一個類可能沒有意義。 如果您有一些性能關鍵代碼並且您不確定是否正在進行裝箱和拆箱,請使用工具檢查方法的IL代碼。 你會在IL中看到單詞box和unbox。 就個人而言,我會衡量我的代碼的性能,然后再看看這是否是一個擔心的候選人。 在你的情況下,我不認為這將是一個如此關鍵的問題。 每次在引用類型中訪問此值類型時,您都不必從堆棧復制到堆(框)。 那種情況是拳擊成為一個更有意義的問題。

  • 答案1:堆。 從他出色的'Essential .Net Vol 1'中詮釋Don Box

引用類型(RT)始終生成在堆上分配的實例。 相反,值類型(VT)取決於上下文 - 如果本地var是VT,則CLR在堆棧上分配內存。 如果類中的字段是VT的成員,則CLR為實例分配內存,作為聲明字段的對象/類型的布局的一部分。

  • 答案2:不會。只有通過對象參考/接口指針訪問結構時才會發生拳擊。 obInstance.VT_typedfield不會是框。

    RT變量包含它引用的對象的地址。 2 RT var可以指向同一個對象。 相反,VT變量本身就是實例。 2 VT var不能指向同一個對象(struct)

  • 答案3:Don Box的Essential .net / Jeffrey Richter通過C#的CLR。 我有一份前者的副本......雖然后者可能會更新.Net修訂版

對象中的值類型是否存在於堆棧或堆上?

在堆上。 它們是對象足跡分配的一部分,就像保持引用的指針一樣。

對象中的裝箱/取消裝箱值類型是否值得關注?

這里沒有拳擊。

關於這個主題,是否有任何詳細但可理解的資源?

給Richter的書+1票。

結構類型的變量或其他存儲位置是該類型的公共和私有實例字段的聚合。 特定

struct Foo {public int x,y; int z;}

宣言Foo bar; 會造成bar.xbar.ybar.z到哪里存放bar將被保存。 從存儲布局的角度來看,向類添加這樣的bar聲明將等同於添加三個int字段。 事實上,如果一個人從來沒有與任何bar除了訪問它的字段,領域的bar將表現一樣將三個字段bar_xbar_ybar_cantaccessthis_z [訪問的最后一個需要與做事bar比訪問其字段等,但它會占用空間,無論它是否真的用於任何東西]。

將結構類型的存儲位置識別為字段的聚合是理解結構的第一步。 試圖將它們視為持有某種對象可能看起來“更簡單”,但與實際工作方式不符。

暫無
暫無

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

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