.Net中的垃圾回收

  • 当某线程触发GC时,任何线程不能再访问任何的对象.
    • 在GC执行时,可能会修改对象的地址.
    • GC执行时,大多使用劫持法,将所有的线程挂起.
    • 对于含有Loop,且Loop内不再调用其它方法时.会生成一个编译时表.当线程的指针指令执行到该处时,会认为到达了一个安全点,线程可以被挂起.
    • 对于其它方法,GC会劫持线程.修改线程堆栈,使其正在执行的方法返回时指向一个CLR内一个特例函数,使得在方法返回时挂起.
    • 当方法慢时,CLR会等待250ms,然后再次检测线程是否为安全点/可被劫持.该过程一直持续到所有线程可被挂起.
    • 只有托管代码可被挂起.而非托管代码已经被固化,会继续执行,然后执行到托管代码时,立即被挂起.
  • 多处理器的优化.
    • 1)托管堆的0代对象被分配到N个内存区内.
    • 2)宿主APP可加载CLR,并使CLR调整GC方式.<runtime><GcServerenabled= true>使托管堆按CPU数划分为N区(即N个托管堆),各区并行执行自己的GC.使用GCSetting.ISServerGC判断.
    • 3)并发收集:<GCConcurrentEnabled =true>在APP运行时,有一普通优先级的后台线程来标记不可达对象,其与APP竞争CPU,当标记完成后即可进行GC.这会造成APP工作集增加,但多CPU时有优势.
    • 4)大尺寸对象:占内存>85KB.被分配到特殊的大尺寸对象堆上,为2代对象,永远不被压缩,但由于其Size未来可变,所以不可假设其地址恒定.应该仅为长期保存的对象保存为大尺寸对象.
  • 代.
    • 基于代的GC的假设:对象越老/新,生存期越长/短.
    • 对堆的部分GC在性能上远大于对整个堆的GC.
    • 堆初始化时不含有对象,第0代对象新构建的对象.在CLR初始化时会給0/1/2代堆一个预算容量:256K/2M/10M.
    • 当0代超量时,第一次GC,之后剩下的对象升格为第一代对象(由第0代对象GC后的残留者),但是如果0代为空,之后再创建的对象为第0代.之后,当0代超量而1代未超,只对0代GC,节省性能.当1代超量后对其GC,之后的剩余者称为2代.GC会自学习APP行为来自动调整各代容量.
  • CLR的内存都是从堆上分配,APP初始化后,CLR留一块连续的地址,保留一指针.
    • NewObject:计算对象Size;再+两个开销(2*32B);分配.对托管堆分配对象仅为在指针上添加一个Value.在未有充足空间时GC.exe.
  • App有一组根(一个存储位置指针,Static字段(类内),引用变量,方法参数,CPU寄存器).
    • 过程:标记(跟Ref一对象,则将对象的同步索引块设一位标记).
    • 压缩(搬移对象以使其内存连续,这样使得引用的根无效,所以重新访问APP的根,修改其指针).
    • 要求:GC能够识别出APP根,还要找到所有对象指针.而C++允许指针转换所以不支持GC,而CLR通过元数据Info总能知道对象的实际类型.
  • 本地资源.
    • 如果用到本地资源,应使用终结以允许GC之前执行一些清理(否则泄露).
    • 1)抽象类CriticalFinalizerObject:首次构造该类型对象时,CLR立即对继承层次中所有Finalize()进行编译以确保终结(当内存小时无法编译之,当方法内引用其他AM中的类型时,可能无法释放本地资源).
    • 2)在调用非Finalize()的Finalize后,再调用Finalize()的终结.
    • 3)在APP域被宿主APP粗鲁中断时自动Finalize().
  • 终止亦可用于托管资源的GC.
    • 但性能:1)由于终结对象的指针被放于终结链上,其分配耗时.2)终结对象会被提升代级别,增大内存压力.3)导致APP变慢(额外的处理开销).
    • 而且Finalize()的执行无法控制且调用顺序不定,调用一个对象的Finalize()时,此对象有可能已被终结,此时出错.  (调用Finalize()的时机:0代对象充满;GC.Collect,Win内存不足,ClR卸载APP域,CLR被关闭).
  • 内部实现.
    • 在New时,如果类型有Finalize(),则在构造实例前,将该对象的指针放到终结链上(Object基类亦有Finalize(),但是已被忽略,仅当本类型/除Object外的基类中定义有Finalize时才算).
    • GC开始后,将终结链上有的不可达对象指针移动到队列上,且对象变为可达不能回收,CLR有一高优先级线程调用Finalize当队列非空时将其唤醒运行.在下次GC时,可以回收刚刚变为可达的对象.但是由于代提升,可能大于2次后,GC才将其回收.
  • 确定的GC(Dispose).
    • 由于Finalize的不定,使用释放模式.接口Idisposable{void Dispose();}但仍有GC负责,释放时机仍不定.
    • 如果类定义了一个实现了该接口的成员字段,则类本身亦需要实现.可以多次调用Dispose/Close(非首次直接返回).
    • Dispose(Bool)CLR调用使用False以使Dispose不再调用其引用的其他托管对象的方法,因为该对象可能已经被Finalize.手动时True可以调用其引用的对象.Dispose为该模式必有的,Close为可选.
    • Dispose只是在一个确定时刻对对象清理资源,而对对象的生命周期没有影响.
    • 在其后调用对象方法会有异常,但是由于对象内存仍然存在,所以不会崩溃.
    • 一般不应该使用Dispose而交由GC自动运行.在需显式释放资源且希望对象从终结链中删除以防止其代提升时使用它.
    • Using语句:初始化一个Object,转化为Idispose调用Dispose(),所以只能用于实现了Idispose接口的类型,可初始化多个同类型的变量.已初始化的对象由于GC不保证多对象上的Finalize顺序,所以应显式调用以避免对象已被终止还在使用.
  • 复苏.
    • 终结可达队列上对象被复苏,且其引用亦复苏.
    • 在Finalize内XXX.static_data=this(重复引用该对象,防止其死亡),GC.ReregisterForFinalize(this)(为了在使用一次该对象后,调用一次Finalize,将对象指针添加到终结链表上以使用).
    • 创建一个托管进程后,在内存中:System/Shared/Default Damain +一系列Heap.
  • 值/引用类型.
    • 值类型.变量中存放的是真正的数据,超出变量的作用域时内存释放,不可为空.
    • 引用类型.变量中存放的是指向数据的指针,由GC负责内存的是否,可为空.
  • CLR有可空值类型.
    • 其本身为轻量级值类型,会造成大量的IL产生.Nullable<T>{bool HasValue,T Value},其可在非Null时拆箱,装箱时会分配内存(为HasValue字段).
    • 装箱:在托管堆上分配内存(各字段和+两额外),值类型字段拷贝至其内,返回对象指针.
    • 拆箱:实为获取一个指向对象中元素值类型的指针.之后会复制字段.

.Net中的垃圾回收,布布扣,bubuko.com

时间: 06-11

.Net中的垃圾回收的相关文章

【java虚拟机序列】java中的垃圾回收与内存分配策略

在[java虚拟机系列]java虚拟机系列之JVM总述中我们已经详细讲解过java中的内存模型,了解了关于JVM中内存管理的基本知识,接下来本博客将带领大家了解java中的垃圾回收与内存分配策略. 垃圾回收(Garbage Collection,GC)是java语言的一大特色,在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM来处理.而在C/C++中是需要程序员主动释放的,而在java中则交给JVM自动完成,既然是交给程序自动执行,那么这里就必须完成以下几件事:

浅谈Chrome V8引擎中的垃圾回收机制

垃圾回收器 JavaScript的垃圾回收器 JavaScript使用垃圾回收机制来自动管理内存.垃圾回收是一把双刃剑,其好处是可以大幅简化程序的内存管理代码,降低程序员的负担,减少因 长时间运转而带来的内存泄露问题.但使用了垃圾回收即意味着程序员将无法掌控内存.ECMAScript没有暴露任何垃圾回收器的接口.我们无法强迫其进 行垃圾回收,更无法干预内存管理 内存管理问题 在浏览器中,Chrome V8引擎实例的生命周期不会很长(谁没事一个页面开着几天几个月不关),而且运行在用户的机器上.如果

Unity优化方向——优化Unity游戏中的垃圾回收

介绍 当我们的游戏运行时,它使用内存来存储数据.当不再需要该数据时,存储该数据的内存将被释放,以便可以重用.垃圾是用来存储数据但不再使用的内存的术语.垃圾回收是该内存再次可用以进行重用的进程的名称. Unity使用垃圾回收作为管理内存的一部分.如果垃圾回收发生得太频繁或者有太多工作要做,我们的游戏可能会表现不佳,这意味着垃圾回收是导致性能问题的常见原因. 在本文中,我们将了解垃圾回收如何工作的,什么时候发生垃圾回收,以及如何有效地使用内存,从而最小化垃圾回收对游戏的影响. 诊断垃圾回收的问题 垃

Python中的垃圾回收机制

当我们声明一个对象的时候,例如str="abcdef",当我们不再使用str这个对象的时候,这个对象就是一个脏对象,垃圾对象,但是它还在占着内存,毕竟我们的电脑内存有限,所以应该有一个机制来回收它以及类似的对象.现在的高级语言如java,c#等,都采用了垃圾收集机制,而不再是c,c++里用户自己管理维护内存的方式.自己管理内存极其自由,可以任意申请内存,但如同一把双刃剑,为大量内存泄露,悬空指针等bug埋下隐患. 对于一个字符串.列表.类甚至数值都是对象,且定位简单易用的语言,自然不会

Java中的垃圾回收

关于垃圾回收,主要是两个步骤: 垃圾对象的判断 垃圾对象的回收 垃圾对象的判断方法 引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象,就是垃圾对象. 可达性分析算法:通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,当一个对象到GC Roots没有任何路径相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则此对象为垃圾对象. 那么,哪些对象可以被认为是“GC Roo

Java和.NET中的垃圾回收机制比较

相同点: 都采用了分代的机制. 都支持并发GC. 都没有采用引用计数方式,而是采用了追踪技术. .NET中,可以通过代码GC.Collect() 强制要求CLR进行垃圾回收(由于垃圾回收是异步的,CLR有一个专用的线程负责垃圾回收,因此,即使调用GC.Collect,也并不是实时的调用了Finalize,因此要保证确实调用了析构方法,可以使用语句GC.WaitForPendingFinalizers()来确保析构方法真的被运行了,参考http://cnn237111.blog.51cto.com

jvm中的垃圾回收

一.垃圾回收的概念 Java中的内存回收即Jvm运行时的内存的回收,需要回收的区域有方法区和Java堆.由于程序计数器,Java虚拟机栈和本地方法栈在方法结束或者是线程结束时会自动进行回收所以无须考虑回收.为什么需要垃圾回收呢?因为电脑中的内存的大小是有限而固定的,在运行过程中由于类的加载和创建,内存中已使用的内存会越来越大,导致后来的程序执行时无法进行分配内存进行执行,此时就需要进行垃圾回收,将已经使用的内存区域中没有在使用的数据清除,从而使后来的程序能够正常运行. 二.如何判断一个对象可以进

第四章 HotSpot jvm 中的垃圾回收机制

有很多有名的Jvm,但我们最常用到的就是Oracle收购sun公司的HotSpot. HotSpot中内存被分为3个代:年轻代(young generation),年老代(old generation),持久代(permanent generation).对象最初在年轻代,年老代代理存放着是经过几次年轻代收集后仍然 live 的对象和一些可能直接被分配到年老代的大对象.持久代存放的是 JVM 认为可以帮助简化垃圾收集管理的对象,比如像类和方法的描述信息. 年轻代代内存模型:一个伊甸区(Eden)

hotspot的JVM中的垃圾回收

有很多有名的Jvm,但我们最常用到的就是Oracle收购sun公司的HotSpot. HotSpot中内存被分为3个代:年轻代(young generation),年老代(old generation),持久代(permanent generation).对象最初在年轻代,年老代代理存放着是经过几次年轻代收集后仍然 live 的对象和一些可能直接被分配到年老代的大对象.持久代存放的是 JVM 认为可以帮助简化垃圾收集管理的对象,比如像类和方法的描述信息. 年轻代代内存模型:一个伊甸区(Eden)