【c#基础】托管 GC 大对象堆 强引用 弱引用 栈
栈:操作系统会为每条线程分配一定的空间,Windows为1M.在栈上的成员不受GC管理器控制,直接由操作系统分配,超出作用域,直接释放。
栈先进后出 执行效率高。
堆:主要用来存放引用类型,由GC管理器托管。GC会处理未引用的堆内存。
堆:后进先出
栈和堆区别:值类型存放在栈中(如果这个值类型放在类中的话,那这个值类型跟类一起存在堆中因为类是引用类型),引用类型存放在堆中(存放的引用对象的值),但还需要在栈中存放一个指向堆中对象的值的地址。
说明:值类型通常时在一些方法参数或者方法内部的局部变量这种是存在栈中的。
使用托管和托管的资源-存储在托管对或本机堆中的对象。
垃圾收集器释放存储在托管堆中的托管对象,但不释放本机堆中的对象,必须由开发人员自己释放。
非托管资源也要由开发人员释放。
在处理器的虚拟内存中,有一个区域称为栈,栈存储的是值数据类型,不是存储对象成员。
在调用方法时,也使用栈存储传递给方法的所有参数的副本。
在释放变量时,释放的顺序总是与给它们分配内存的顺序相反。先分配内存的后释放,后分配内存的先释放。
栈指针表示栈中的下一个空闲存储单元的地址。程序第一次运行时,栈指针指向栈保留的内存块末尾。栈实际上是向下填充的。即从高内存地址向低内存地址填充。堆则相反由低内存地址向高内存地址填充(c语言也是一样的,在深入理解计算机系统中有介绍)
栈有非常高的性能。
引用类型 声明后做了什么:
Customer customer=new Customer();
首先:Customer customer 是在栈上给这个引用分配存储空间,但这仅是一个引用而不是实际的Customer对象。
new Customer()这个代码 是在 堆上分配内存,以存储Customer对象(一个真正的对象,不只是一个地址)。然后 将cunstomer 的值设置为分配给新Customer对象的内存地址(它还调用了合适的Customer()构造函数初始化类实例中的字段)
把一个引用变量的值赋予另一个相同类型的变量,就有两个变量引用内存中的同一对象了。当一个引用变量超出作用域时,他会从栈中删除,但引用的对象数据仍保留在栈中,一直到该数据不再被任何变量引用就会被垃圾回收器删除.或程序终止。
垃圾回收器运行时,它会从堆中删除不再引用的对象。垃圾回收器在引用的根表中找到所有引用的对象,接着在引用的对象树中查找。在完成删除操作后,堆会立即吧对象分散开,与已经释放的内存混合在一起。
垃圾回收器压缩:垃圾回收器会把其他对象移动回到端部,再次形成连续的内存块。
垃圾回收器的这个压缩操作是托管的堆与非托管的堆区别所在。使用托管的堆,就只需要读取堆指针的值即可,不需要便利地址的链表。
需要进行垃圾回收时,可以调用System.GC.Collcet()方法,强迫垃圾回收器在代码的某个地方运行回收垃圾。
在测试中运行GC比较有用,这样看到应该回收的对象仍然未回收而导致内存泄漏。
GC垃圾回收总的有三代:0,1,2
创建对象时,把对象放在托管堆上,堆的第一部分称为0代。
经过第一次垃圾回收后,仍然保留的话对象会先被压缩,然后移动第一代对应的部分,现在是第一代。
重复下一次回收过程,第一代遗留下来的还没被回收的会移动到第2代。
新创建的对象都是0代。
这个过程回极大的提高应用程序的性能。一般而言。最新的对象通常是可以回收的对象。
如果对象在堆中的位置是相邻的,垃圾回收过程就会更快。
在.Net中,垃圾回收提高性能的另一个领域是架构处理堆上较大的对象方式。
在.Net下,较大的对象有自己的托管堆,称为大对象堆。使用大于85000个字节 的对象时,它们就会放在这个特殊的堆上,而不是主堆上。
.Net应用程序不知道两者的区别,因为这是自动完成的。
为什么要有大对象堆?
因为在堆上压缩大对象是比较昂贵的,因此驻留在大对象堆上的对象不执行压缩过程。
第二代和大对象堆上的回收现在是放在后台线程上进行的。
这表示应用程序线程仅会为第0代和第1代的回收而阻塞,减少了总暂停的时间,服务器和工作站默认打开这个功能。
有利于提高应用程序性能的另一个优化是:垃圾回收的平衡。它专用域服务器的垃圾回收。
服务器一般有一个线程池,执行大致相同的工作,内存分配在所有线程上都是类似的。
每个逻辑服务器都有一个垃圾回收堆。因此其中一个堆用尽了内存,触发了垃圾回收过程时,所有堆也可能会得益于垃圾回收,这就不是很高效,垃圾回收过程会平衡这些堆----小对象堆和大对象堆。进行这个平衡过程,可以减少不必要的回收。
为了利用包含大量内存的硬件,垃圾回收过程添加了GCSettings.LatencyMode属性。
把这个属性设置为GCLatencyMode 枚举的一个值,可以控制垃圾回收器进行回收的方式。
LowLatency和NoGCRegion设置使用的时间应为最小值,分配的内存量应尽可能小,如果不小心,就可以能出现溢出内存。
强引用和弱引用
垃圾回收器不能回收仍在引用的对象的内存--这是一个强引用。它可以回收不再跟表中直接或间接引用的托管内存。有时可能会忘记释放引用。
如果对象相互引用,但没有在根表中引用,例如对象A引用对象B,B引用C,C引用A,则GC可以销毁所有这些对象。
使用弱引用(WeakReference)可以避免忘记释放这种情况。使用事件很容易错过引用的清理。
弱引用对小对象没有意义,因为弱引用有自己的开销,这个开销可能比小对象更大。
弱引用是使用WeakReferenc类创建的。使用构造函数,可以传递强引用。
例子:
var myWeakReference=new WeakReference(new DataObject()) if(myWeakReference.IsAlive) { DataObjecet strongReferenct=myWeakReference.Target as DataObject if(strongReferenct!=null) { ..... } } else { //.... }