C# GC

一 GC(垃圾回收)的概念

了解:创建对象的主要流程

  1. 计算要分配的内存大下
  2. 向GC堆申请内存
  3. 检查GC堆的NextObjPtr(0代GC堆)后面内存是否足够
  4. 内存不足就触发一次0代垃圾回收
  5. 分配内存,后移NextObjPtr指针,使之永远指向下一个可用地址
  6. 调用构造函数

1.1堆与GC

 

  • 加载堆:盛放了静态类型及成员,程序运行的任何时候都不可能是垃圾,不受GC管辖
  • GC堆:GC只负责GC堆的清理。GC堆初始化时有阈值,256K,2M,10M,可动态调整。
  • 大对象堆:2代GC堆一部分,不会轻易回收,对象太大,移动复制成本高。

注意:

  1. 大部分情况,自动GC只对0代起作用,(其它代满也会)回收后的对象,存活下来的,会升代
  2. 代的数目太多时,导致0代空间变小,更容易满,会令自动GC更容易触发,升代更频繁

1.2识别垃圾

了解局部性原理:时间局部性,空间局部性

垃圾定义:没有被其它任何资源引用的资源。

判别方法(常用的):

  • 引用计数法:在任何时候,维护任意一个类型的引用数。若该数为0,视为垃圾。
  • 标记法:需要GC时,建立一张引用有向图,从若干起点开始遍历,无法到达的对象皆为垃圾。

引用计数法

引用计数法的优点:

  1. 实时垃圾处理:引用数变为0时(即成为垃圾的那一刻),立即回收
  2. 垃圾回收性能很高:一次仅操作一个对象(与标记法相比)

缺陷:无法解决循环引用

循环引用:数个对象互相引用对方,形成一个环,以至于引用始终不为0,无法被GC清理。

优化:弱引用(了解)

标记压缩法

处理步骤

  1. 标记:为堆上所有的对象建立一个有向图,垃圾收集器可以检测到所有的孤岛,将他们标识为垃圾。(主要:根引用,根指针列表,根不会为垃圾,发现标记过即为循环引用)
  2. 压缩:将非垃圾对象进行移动,使得堆中的空间尽量连续。(GC时,所有线程停止工作:压缩时,对象的指针会暂时指向无效地址,可能指针悬挂)

优点

解决循环引用

缺点

无法做到实时清除

注意:C#使用标记压缩法

触发GC的情况

  1. 0代没有足够空间
  2. 主动调用了GC.Collect(可以指定代)
  3. 卸载AppDomain
  4. Windows报告内存不足

二垃圾回收策略

 垃圾回收的一般方式:

  1. Dispose方法和IDispose接口(非托管的第一道闸):通过继承IDispose接口,并实现Disposible方法,可以对托管和非托管资源进行回收。当类型中含有非托管资源或者实现了Disposible,才需要继承IDisposible接口。
  2. 析构函数(Finalize的语法糖)(终结器)(非托管的二道闸,结构体除外):在类型中含有非托管时才需要实现析构函数。声明析构函数的唯一目的是回收非托管资源。:如果类型中有非托管资源,却没有实现析构函数,有没有调用Dispose,则当GC回收这个类型时(通过Finalize),将只会回收托管资源(非托管资源没有FInalize方法),非托管资源将会一直存留在堆中。

可终结对象的复生及避免:

可终结对象:若一个对象拥有Finalize方法,那这个对象就被认为是可终结的。终结该对象时,会调用他的Finalize方法。调用者是一个优先级较高的线程。

直接上图:

 

 一轮GC之前,终结列表不是根,否则,C无法被GC掉,现在,C是垃圾

 

 一轮GC之后,C不是垃圾,因为被freachable队列中的一个对象引用,而freachable队列是一组根

#空间换时间

移到freachable队列时:

  • 是为了保证调用Finalize方法,复生是他的副作用
  • 发生在标记阶段
  • 当reachable队列出现任何记录时,执行该记录的Finalize方法。

#可终结队形至少能存活两轮GC.

#解决方法:主动调用Dispose方法,Dispose方法成功执行之后,调用GC.SuppressFinalize告诉GC不需要再去调用这个对象的Finalize.

#另:GC.ReRegisterForFinalize方法,可以将对象放入freachable队列中。(成为不死C)

回收托管和非托管资源

都基于垃圾回收的一般方式。
非托管有Dispose模式。

#详解:《C#从现象到本质》p142

using关键字

使用using关键字可以保证对象离开using块时,CLR会自动调用他的Dispose方法,所以,显然,using只能用于那些实现了IDisposible接口的对象。

using会被编译为try-finaly块,在finaly中调用Dispose方法。

我们不需要手动调用Dispose对象,只要代码离开using块,自动调用Dispose方法。

 

 

 

posted @   肆空界  阅读(610)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示