首先说bai下C#中的变量类型吧,duC#中有2个变量类zhi型,一种是值类型,一dao种是引用类型,值类型是zhuan在栈上创建shu,这一类型用不到GC,引用类型是在堆中创建,GC主要是在这里管理对象。GC对每个对象有个引用计数,所有说只要有变量在引用它,计数器就不为了,一个变量不再引用这个对象,对象的计数器就减一,知道计数器为0时,对象就成为内存垃圾了(没有变量引用它),但是此时垃圾并没有回收。那什么时候回收呢,是在内存占用超过一定限度是,GC才启动,释放垃圾资源,说白了就是delete这些对象,将空间归还给系统。但是这还没完,空间释放后,内存空间就不连续了,所有GC还要赶一件事,就是将空间整理下,将占用的空间连续话,具体说就是将空间向上推,就是想高地值转存,这样空间就连续了,使用也方便了,然后GC就改变应用那些对象的变量里地地址,让他们指向正确的位置,所以说C#中的引用类型就是一种指针,一种动态改变值的指针。什么是GCGC(Garbage Collector)就是垃圾收集器,这里仅就内存而言。以应用程序的root为基础,遍历应用程序在Heap上动态分配的所有对象,通过识别它们是否被引用来确定哪些对象是已经死亡的、哪些仍需要被使用。已经不再被应用程序的root或者别的对象所引用的对象就是已经死亡的对象,即所谓的垃圾,需要被回收。这就是GC工作的原理。 为了实现这个原理,GC有多种算法。比较常见的算法有Reference Counting,Mark Sweep,Copy Collection等等。目前主流的虚拟系统.NET CLR,Java VM都是采用的Mark Sweep算法。 算法工作原理垃圾收集器的本质,就是跟踪所有被引用到的对象,整理不再被引用的对象,回收相应的内存。 这听起来类似于一种叫做“引用计数(Reference Counting)”的算法,然而这种算法需要遍历所有对象,并维护它们的引用情况,所以效率较低些,并且在出现“环引用”时很容易造成内存泄露。 所以.Net中采用了一种叫做“标记与清除(Mark Sweep)”算法来完成上述任务。
简单地把.NET的GC算法看作Mark-Sweep 算法。 Mark-Sweep 算法.png Reachable objects:指根据对象引用关系,从roots出发可以到达的对象。例如当前执行函数的局部变量对象A是一个root object,他的成员变量引用了对象B,则B是一个reachable object。从roots出发可以创建reachable objects graph,剩余对象即为unreachable,可以被回收 。 GC按什么规则收集垃圾对象--Generational 分代算法?.NET将heap分成3个代龄区域: Gen 0、Gen 1、Gen 2;heap分配的对象是连续的,关联度较强有利于提高CPU cache的命中率。 Generational 分代算法.png Heap分为3个代龄区域,相应的GC有3种方式: # Gen 0 collections, # Gen 1 collections, #Gen 2 collections。 如果Gen 0 heap内存达到阀值,则触发0代GC,0代GC后Gen 0中幸存的对象进入Gen1。如果Gen 1的内存达到阀值,则进行1代GC,1代GC将Gen 0 heap和Gen 1 heap一起进行回收,幸存的对象进入Gen2。2代GC将Gen 0 heap、Gen 1 heap和Gen 2 heap一起回收。如果GC跑过了,内存空间依然不够用,那么就抛出了OutOfMemoryException异常。 Gen 0和Gen 1比较小,这两个代龄加起来总是保持在16M左右;Gen2的大小由应用程序确定,可能达到几G,因此0代和1代GC的成本非常低,2代GC称为full GC,通常成本很高。 粗略的计算0代和1代GC应当能在几毫秒到几十毫秒之间完成,Gen 2 heap比较大时,full GC可能需要花费几秒时间。大致上来讲.NET应用运行期间,2代、1代和0代GC的频率应当大致为1:10:100。 该如何释放非托管资源呢?既然有了垃圾收集器,为什么还要Dispose方法和析构函数? 如果我们不想为一个类实现Dispose方法,而是想让它自动的释放非托管资源,那么就要用到析构函数了。析构函数是由GC调用的。你无法预测析构函数何时会被调用,所以尽量不要在这里操作可能被回收的托管资源,析构函数只用来释放非托管资源。GC释放包含析构函数的对象,需要垃圾处理器调用俩次,CLR会先让析构函数执行,再收集它占用的内存。 什么场景下手动执行垃圾收集?GC什么时候执行垃圾收集是一个非常复杂的算法(策略),大概可以描述成这样:如果GC发现上一次收集了很多对象,释放了很大的内存,那么它就会尽快执行第二次回收,如果它频繁的回收,但释放的内存不多,那么它就会减慢回收的频率。所以,尽量不要调用GC.Collect()这样会破坏GC现有的执行策略。除非你对你的应用程序内存使用情况非常了解,你知道何时会产生大量的垃圾,那么你可以手动干预垃圾收集器的工作,例如我有一个大对象,我担心GC要过很久才会收集他。 GC.Collect() 方法作用:强制进行垃圾回收。
GC注意事项:
为什么要使用GC呢?
总的说来GC可以使程序员可以从复杂的内存问题中摆脱出来,从而提高了软件开发的速度、质量和安全性。 |
|
来自: ontheroad96j47 > 《待分类》