基础一 基础语法 内存管理和垃圾回收
摘抄 https://www.cnblogs.com/edisonchou/p/4787775.html
1.NET中栈和堆的差异
每一个.NET应用程序最终都会运行在一个OS进程中,假设这个OS的传统的32位系统,那么每个.NET应用程序都可以拥有一个4GB的虚拟内存。.NET会在这个4GB的虚拟内存块中开辟三块内存作为 堆栈、托管堆 以及 非托管堆。
(1).NET中的堆栈
堆栈用来存储值类型的对象和引用类型对象的引用(地址),其分配的是一块连续的地址,如下图所示,在.NET应用程序中,堆栈上的地址从高位向低位分配内存,.NET只需要保存一个指针指向下一个未分配内存的内存地址即可。
对于所有需要分配的对象,会依次分配到堆栈中,其释放也会严格按照栈的逻辑(FILO,先进后出)依次进行退栈。(这里的“依次”是指按照变量的作用域进行的),假设有以下一段代码:
TempClass a = new TempClass(); a.numA = 1; a.numB = 2;
其在堆栈中的内存图如下图所示:
这里TempClass是一个引用类型,拥有两个整型的int成员,在栈中依次需要分配的是a的引用,a.numA和a.numB。当a的作用域结束之后,这三个会按照a.numB→a.numA→a的顺序依次退栈。
(2).NET中的托管堆
众所周知,.NET中的引用类型对象时分配在托管堆上的,和堆栈一样,托管堆也是进程内存空间中的一块区域。But,托管堆的内存分配却和堆栈有很大区别。受益于.NET内存管理机制,托管堆的分配也是连续的(从低位到高位),但是堆中却存在着暂时不能被分配却已经无用的对象内存块。
如上图所示,.NET程序通过分配在堆栈中的引用来找到分配在托管堆的对象实例。当堆栈中的引用退出作用域时,这时仅仅就断开和实际对象实例的引用联系。而当托管堆中的内存不够时,.NET会开始执行GC(垃圾回收)机制。GC是一个非常复杂的过程,它不仅涉及托管堆中对象的释放,而且需要移动合并托管堆中的内存块。当GC之后,堆中不再被使用的对象实例才会被部分释放(注意并不是完全释放),而在这之前,它们在堆中是暂时不可用的。在C/C++中,由于没有GC,因此可以直接free/delete来释放内存。
(3).NET中的非托管堆
.NET程序还包含了非托管堆,所有需要分配堆内存的非托管资源将会被分配到非托管堆上。非托管的堆需要程序员用指针手动地分配和释放内存,.NET中的GC和内存管理不适用于非托管堆,其内存块也不会被合并移动,所以非托管堆的内存分配是按块的、不连续的。因此,这也解释了我们为何在使用非托管资源(如:文件流、数据库连接等)需要手动地调用Dispose()方法进行内存释放的原因。
(4)如何判断托管和非托管
托管堆,CG会自动回收,而非托管堆,CG不会自动回收,需要手动调用Dispose()方法释放内存
非托管的堆有:文件操作,数据库连接,网络连接 等
.Net Framework 是由彼此独立又相关的两部分组成:CLR 和 类库, CLR是它为我们提供的服务,类库是它实现的功能. .NET的大部分特性----垃圾收集,版本控制,线程管理等,都使用了CLR提供的服务
如何判断是否是托管
在工程里点击引用,添加引用,
你会看到页签 .net里面的全是托管对像,而 com对像的全是非托管对象。
托管代码不用关心它的垃圾回收和内存管理
非托管代码用文件流,数据库连接,不用的时候需要手动 dispose
2. 垃圾回收机制
对象销毁
1.对象的生命周期:对象在内存中不断的“生生死死”,具有生命周期
2.对象在内存中的状态
正在使用--------》 程序正在使用的对象
游离状态 -------》没有引用的对象,已经使用完毕但依然占据空间
3. 垃圾回收机制(GC)
Net虚拟机特有的机制,自动运行,并检查对象的状态
发现对象不再引用,会将其释放所占用的空间(销毁)
4.手动销毁对象:obj.Dispose(); ,适用于资源占用大的程序,Dispose()方法自定义的对象不实现,想要实现需要在定义类中实现析构函数,这里不做了解。
5. using使用,只有当对象使用了IDispose接口后在可以使用,表示对象使用完立即销毁,即在末尾调用了Dispose()方法
using 对自定义的对象不能使用,除非自定义对象实现了IDispose接口(即实现析构函数)
6. 可以给对象赋值 null ,GC 会自动回收,自定义的对象赋值为null后,CG 也回收,因为所有自定义的对象都是托管对象