.net托管内存如何处理对象内部的值类型?
public class MyClass { public int Age; public int ID; } public void MyMethod() { MyClass m = new MyClass(); int newID; }
据我了解,以下是正确的:
- 引用m位于堆栈上,并且在MyMethod()退出时超出范围。
- 值类型newID驻留在堆栈上,并在MyMethod()退出时超出范围。
- 假设不存在对该对象的其他引用,则由新运算符创建的对象将驻留在堆中,并且在MyMethod()退出时可由GC回收。
这是我的问题:
- 对象中的值类型是否存在于堆栈或者堆中?
- 装箱/拆箱对象中的值类型是否值得关注?
- 是否有关于此主题的详细但可理解的资源?
从逻辑上讲,我认为类内的值类型应该在堆中,但是我不确定是否必须将它们装箱才能到达那里。
编辑:
建议阅读该主题:
- 杰弗里·里希特(Jeffrey Richter)通过C#进行CLR
- Don Box的Essential .NET
解决方案
回答
- 对象拥有的所有引用或者值类型都存在于堆中。
- 仅当我们将int强制转换为Objects时。
回答
我所见过的最好的资源是Cby Jeffrey Richter所著的CLR。如果我们进行任何.NET开发,都非常值得一读。基于该文本,我的理解是引用类型内的值类型确实存在于父对象中嵌入的堆中。引用类型总是在堆上。
装箱和拆箱不是对称的。与拆箱相比,装箱可能是更大的问题。装箱将需要将值类型的内容从堆栈复制到堆。根据发生这种情况的频率,可能没有必要使用结构而不是类。如果我们有一些对性能有严格要求的代码,并且不确定装箱和拆箱是否正在发生,请使用工具检查我们方法的IL代码。我们会在IL中看到单词框和框框。就个人而言,我将评估代码的性能,然后再查看是否值得担心。就我们而言,我认为这不是一个关键问题。每次访问引用类型内的该值类型时,我们都不必从堆栈复制到堆(盒子)。在这种情况下,拳击成为更有意义的问题。
回答
Do value types within objects live on the stack or the heap?
在堆上。它们是对象覆盖区分配的一部分,就像保存引用的指针一样。
Is boxing/unboxing value types in an object a concern?
这里没有拳击。
Are there any detailed, yet understandable, resources on this topic?
对Richter的书+1票。
回答
类的值类型值必须与对象实例一起存在于托管堆中。方法的线程堆栈仅在方法持续时间内有效;如果值仅存在于该堆栈中,该值如何持久?
托管堆中类的对象大小是其值类型字段,引用类型指针和其他CLR开销变量(如Sync块索引)的总和。当人们为一个对象的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:堆。从Don Box出色的《 Essential .Net Vol 1》中释义
Reference Types(RT) always yield instances that are allocated on the heap. In contrast, value types(VT) are dependent on the context - If a local var is a VT, the CLR allocates memory on the stack. If a field in a class is a member of a VT, then the CLR allocates memory for the instance as part of the layout of the object/Type in which field is declared.
- 答案2:否。仅当我们通过对象引用/接口指针访问结构时,才会进行装箱。 obInstance.VT_typedfield不会装箱。
RT variables contains the address of the object it refers to. 2 RT var can point to the same object. In contrast, VT variables are the instances themselves. 2 VT var cannot point to same object(struct)
- 答案3:Don Box的Essential .net /杰弗里·里希特(Jeffrey Richter)通过C#进行的CLR。我有前者的副本...尽管以后可能会针对.Net修订版进行更多更新