你的位置:首页 > ASP.net教程

[ASP.net教程].NET 类型(Types)的那些事


引言

您是.Net工程师?那 .NetFramework中的类型您知道有三大类吗?(除了引用类型和值类型,还有?)

引用类型一定在“堆”上,值类型一定在“栈”上?

那引用类型在内存中的布局细节您又知道多少了?

本篇就将这些问题一一道来

 

.Net Framework 中的Types分类

 

09fig01

C# type categorization. 带阴影的都是 C# 的内建类型关键字.

除了object and string(分别为System.Object 和System.String别名), 其他带阴影的都是简单的值类型.

下面是摘自《C#语言规范5.0》 –> 4.类型(page:77)

C# 语言的类型划分为两大类:值类型 (Value type) 和引用类型 (reference type)。

第三种类型是指针,只能用在不安全代码中。

引用类型和值类型在内存中如何分配的呢?

这一块我们将通过一小段代码来讲解,在讲解前让我们回顾下

引用类型 和值类型的赋值过程中在内存处理上的区别:

  • 把一个值类型a(定义如下int a=80;)赋给另外一个值类型b(int b;),即(b=a;)时,会把a的值拷贝一份给a,如下图;

value_type

  • 把一个引用类型a(定义如下Employee a=new employee();)赋给另外一个值类型b(Employee b;),即(b=a;)时,会把a的地址(引用)拷贝一份给a,即他们指向同一个地址;

reference_type1

开始代码讲解,首先看代码如下:

Form myForm = new Form();Size s = new Size (100, 100);     // struct = value typeFont f = new Font (“Arial”,10);    // class = reference typemyForm.Size = s;myForm.Font = f;

注意代码中 myForm.Size中的Size和myForm.Font的Font是Form类型的属性(Property)不是类型(class type,代表某个类型)。

在.NetFramework中这样的使用方式极其普遍,初学者不要混淆了这两者。

讲解代码前给大家再提下知识点:

  • Size是Struct类型,当然就是值类型(ValueType)
  • Font是Class类型,当然就是引用类型(ReferenceType)

 

上面这段代码的内存中的分配,示意图:

image006

很清楚地看到

  • Size类型的s分配到了Stack上,而Front类型的f 和Form类型的myForm则分配在堆上。
  • 并且myForm的Font属性引用到了Font类型f。
  • myForm的Size属性有它自己的值(Width和Height),它是Size类型s的一个拷贝。

这里我们更清晰的看到了值类型和引用类型在值赋值过程中的区别

我们可以通过修改Font类型f的值,来修改myForm中的字体样式,但不能通过修改s来修改myFrom的Size。

引用类型的Object内存布局基础结构

image_thumb_2

上面这个图展示的结构是通过分析源码得出的:

  1. 首先ObjectHeader(在其所在的AppDomain中的那个Thread通过调用Monitor.Enter锁了这个对象)
  2. 接下来是Method Table 指针(该指针指向AppDomain中声明(定义)托管类型),如果程序集被加载到AppDomain neutral 中,那么所有的AppDomain中该类型实例的Method Table指针都一样。CLR 类型系统的该基础构建块在托管代码中都是可视的。(TypeHandle.Value 是一个IntPtr)
  3. 最后就是这部分就是该类型实例的值。

CLR object的这个实例对象的地址在垃圾回收时也有可能会发生变动。(具体参看GC中压缩过程)

 

\sscli20\clr\src\vm\object.h

//// The generational GC requires that every object be at least 12 bytes// in size.  #define MIN_OBJECT_SIZE   (2*sizeof(BYTE*) + sizeof(ObjHeader))

A .NET object has basically this layout:

class Object{ protected:  MethodTable*  m_pMethTab;};
class ObjHeader{ private:  // !!! Notice: m_SyncBlockValue *MUST* be the last field in ObjHeader.  DWORD m_SyncBlockValue;   // the Index and the Bits};
Platform最小实例大小(bytes)
x8612 bytes = 2*4+4
x64 24 bytes = 2*8+8

 

为方便大家查看源码,这里提供一个源码索引表

SSCLI文件索引

ItemSSCLI Path
AppDomain\sscli\clr\src\vm\appdomain.hpp
AppDomainStringLiteralMap\sscli\clr\src\vm\stringliteralmap.h
BaseDomain\sscli\clr\src\vm\appdomain.hpp
ClassLoader\sscli\clr\src\vm\clsload.hpp
EEClass\sscli\clr\src\vm\class.h
FieldDescs\sscli\clr\src\vm\field.h
GCHeap\sscli\clr\src\vm\gc.h
GlobalStringLiteralMap\sscli\clr\src\vm\stringliteralmap.h
HandleTable\sscli\clr\src\vm\handletable.h
InterfaceVTableMapMgr\sscli\clr\src\vm\appdomain.hpp
Large Object Heap\sscli\clr\src\vm\gc.h
LayoutKind\sscli\clr\src\bcl\system\runtime\interopservices\layoutkind.cs
LoaderHeaps\sscli\clr\src\inc\utilcode.h
MethodDescs\sscli\clr\src\vm\method.hpp
MethodTables\sscli\clr\src\vm\class.h
OBJECTREF\sscli\clr\src\vm\typehandle.h
SecurityContext\sscli\clr\src\vm\security.h
SecurityDescriptor\sscli\clr\src\vm\security.h
SharedDomain\sscli\clr\src\vm\appdomain.hpp
StructLayoutAttribute\sscli\clr\src\bcl\system\runtime\interopservices\attributes.cs
SyncTableEntry\sscli\clr\src\vm\syncblk.h
System namespace\sscli\clr\src\bcl\system
SystemDomain\sscli\clr\src\vm\appdomain.hpp
TypeHandle\sscli\clr\src\vm\typehandle.h

 

引用类型的Object内存布局代表性结构

 

普通对象

 

012302077246530

数组对象 - Array

012302218801605

字符串对象

012302322553069

 

参考

 

The Truth About .NET Objects And Sharing Them Between AppDomains

Six important .NET concepts: Stack, heap, value types, reference types, boxing, and unboxing

Shared Source Common Language Infrastructure

[翻译经典文章]深入.NET Framework内部, 看看CLR如何创建运行时对象的

.NET对象的内存布局

托管堆与垃圾收集