本篇用来梳理对象的创建、垃圾的回收,以及非托管资源的手动处理。
→首先运行应用程序,创建一个Windows进程。
→CLR创建一块连续的虚拟地址空间,这个地址空间就是托管堆。而且,这个地址空间最初并没有对应的物理存储空间。
虚拟地址空间分成2段。一个区段是普通堆,也叫GC堆,大小小于85000字节的引用类型对象的实例被分配在这里;另一个是大对象堆,大小大于等于85000字节的引用类型对象的实例被分配在这里。
对于客户端应用程序,每个区段的大小大致是16MB;对于服务端应用程序,每个区段的大小大致是64MB。另外,每个区段的大小还会因CPU的数量,是否是32位或64位操作系统而各异。
随着每个区段被装满对象,CLR会分配更多的区段,直到整个进程空间都满了为止,每个进程可使用4GB的内存。
托管堆上维护着一个指针NextObjPtr,指向下一个对象在托管堆上分配的位置。
托管堆根据存储信息的不同分为垃圾回收堆GC Heap和加载堆Loader Heap。垃圾回收堆GC Heap存储对象实例,受GC管理;加载堆Loader Heap存储AppDomain中的元数据信息,例如基类型、静态字段、静态方法、接口信息等,不受GC管理,它的生命周期从创建AppDomain开始到卸载AppDomain结束。此时,托管堆以及其它方面大致是这样分布的:
→在托管堆上创建对象
□ 引用类型对象创建
使用new关键字创建引用类型对象。
FileStream fs = new FileStream(@"",FileMode.Open);
以上代码经编译器编译,在生成的中间IL代码中实际上是一个newobj指令。IL相关的指令还包括:ldstr指令用于创建string类型对象,newarr用于分配新的数组对象,box指令用于在值类型转换为引用类型时,将值类型字段拷贝到托管堆上。
通过以下代码来体会托管堆上创建对象的大致过程:
public class Employee{PRivate int _id;private Status _status;public Employee(){_id = 1;_status = new Status();}}public class Sales : Employee{public bool _isLive;public bool IsLive(){return _isLive;}public static void Main(){Sales _sales;_sales = new Sales();_sales._isLive = true;Console.WriteLine(_sales.IsLive());}}public class Status{private int _years;private char _level = "A";}
1、执行Sales _sales,在线程堆栈上开辟4byte的内存空间,用于保存Sales对象的托管堆地址,此时为null。
2、_sales = new Sales(),递归
新闻热点
疑难解答