惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Help Net Security
Help Net Security
G
Google Developers Blog
雷峰网
雷峰网
WordPress大学
WordPress大学
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Engineering at Meta
Engineering at Meta
Security Latest
Security Latest
T
Threat Research - Cisco Blogs
AWS News Blog
AWS News Blog
F
Full Disclosure
C
Cybersecurity and Infrastructure Security Agency CISA
T
The Exploit Database - CXSecurity.com
J
Java Code Geeks
U
Unit 42
C
Cyber Attacks, Cyber Crime and Cyber Security
V
V2EX
C
Cisco Blogs
博客园 - 司徒正美
Project Zero
Project Zero
L
LINUX DO - 热门话题
阮一峰的网络日志
阮一峰的网络日志
Blog — PlanetScale
Blog — PlanetScale
Scott Helme
Scott Helme
A
About on SuperTechFans
Hugging Face - Blog
Hugging Face - Blog
S
Securelist
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
S
Schneier on Security
G
GRAHAM CLULEY
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyberwarzone
Cyberwarzone
MongoDB | Blog
MongoDB | Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 叶小钗
T
Threatpost
Recorded Future
Recorded Future
C
CXSECURITY Database RSS Feed - CXSecurity.com
宝玉的分享
宝玉的分享
N
News and Events Feed by Topic
人人都是产品经理
人人都是产品经理
The Register - Security
The Register - Security
S
Security Archives - TechRepublic
博客园 - Franky
N
News | PayPal Newsroom
Simon Willison's Weblog
Simon Willison's Weblog
S
SegmentFault 最新的问题
W
WeLiveSecurity
A
Arctic Wolf
B
Blog

博客园 - 颜昌钢

怎样做好一个项目经理? ICollection 与 IList 区别 oracle 数据库跨库查询方法 SetTimeOut 与 SetInterval 区别 - 颜昌钢 内存释放 机制 remoting 学习整理 wpf windows 放大缩小 疑问?? - 颜昌钢 WPF中代码画箭头 方法参数 Ref 与 引用类型 DataTrigger 以及 EnterActions 和ExitActions 学习笔记 - 颜昌钢 WPF xaml文件中使用大括号{} - 颜昌钢 - 博客园 WPF下的地图解决方案 关于WPF的Binding 的 ConverterParameter 参数的动态设置 关于 项目 投标相关文档 服务器计时器、Windows 计时器和线程计时器 wpf小控件 集合 仪表盘等 wpf button 事件的触发顺序 - 颜昌钢 c# 插件 体系 一个类似于 splittercontainer 控件的控件
GC 资源 回收
颜昌钢 · 2010-04-05 · via 博客园 - 颜昌钢

2010年2月16日 星期二 20时39分

 如下内容,是自己乱想的,不代表正确性的。。

.net中资源(相对于内存来说)分两种,托管资源和非托管资源。

非托管资源,有形如如下的几种:数据库、文件等

在.net中,托管资源由CLR控制,由GC(Garbage Collector)负责释放。非托管资源,一般需要实现IDisposable来负责实现资源的回收释放。


堆内存分配

到第一个有足够空间的内存地址(没被占用的),然后将该内存分配。

托管堆中内存的分配是顺序的,也就是说一个挨着一个的分配。这样内存分配的速度就要比原始程序高,但是高出的速度会被GC找回去


GC工作方式

托管代码中的对象什么时候回收我们管不了(除非用GC.Collect强迫GC回收)。GC会在它"高兴"的时候执行一次回收(这有许多原因,比如内存不够用时。这样做是为了提高内存分配、回收的效率)。那么如果用Destructor呢?同样不行,因为.NET中Destructor的概念已经不存在了,它变成了Finalizer,为什么呢?在.NET中由于GC的特殊工作方式,Destructor并不实际存在,事实上,当调用Destructor的语法时,编译器会自动将它写为protected virtual void Finalize();

GC步骤

1:确定对象没有任何引用
2:检查对象是否在Finalizer表上有记录(如果类定义的时候,提供了析构函数,则在实例化对象的时候,则会在Finalizer表中增加一条记录)
3:如果在Finalizer表上有记录,那么将记录移到另外的一张表上(Finalizer2)。
4:如果不在Finalizer2表上有记录,那么释放内存

对于第4步,Finalizer2中的记录,又是什么时候删除呢?
   在Finalizer2表上的对象的Finalizer会在另外一个low priority的线程上执行后从表上删除

从如上的步骤,可以看出,有析构函数的对象,不会马上被回收(代码1),只有执行一次以上的GC回收,才会被回收。

所以说,除非是绝对的需要,请不要提供析构函数。


代码1:

public class CountObject {
  public static int Count = 0;

  public CountObject() {
    Count++;
  }

  ~CountObject() {
    Count--;
  }
}

static void Main() {
  CountObject obj;
  for (int i = 0; i < 5; i++) {
    // GC.Collect();(就算执行 GC.Collect,也不会被删除)
    obj = null; // 这一步多余,这么写只是为了更清晰些!
   obj = new CountObject();
  }

  // Count不会是1,因为Finalizer不会马上被触发,要等到有一次回收操作后才会被触发。
  Console.WriteLine(CountObject.Count);
  Console.ReadLine();
}
 

GC为了提高回收的效率使用了Generation的概念,原理是这样的,第一次回收之前创建的对象属于Generation 0,之后,每次回收时这个Generation的号码就会向后挪一,也就是说,第二次回收时原来的Generation 0变成了Generation 1,而在第一次回收后和第二次回收前创建的对象将属于Generation 0。GC会先试着在属于Generation 0的对象中回收,因为这些是最新的,所以最有可能会被回收,比如一些函数中的局部变量在退出函数时就没有引用了(可被回收)。如果在Generation 0中回收了足够的内存,那么GC就不会再接着回收了,如果回收的还不够,那么GC就试着在Generation 1中回收,如果还不够就在Generation 2中回收,以此类推。Generation也有个最大限制,根据Framework版本而定,可以用GC.MaxGeneration获得。在回收了内存之后GC会重新排整内存,让数据间没有空格,这样是因为CLR顺序分配内存,所以内存之间不能有空着的内存。现在我们知道每次回收时都会浪费一定的CPU时间,所以,不推荐手动GC.Collect。
 

非托管资源的释放

IDisposable接口,按照.NET Framework的标准,所有有需要手动释放非托管资源的类都得实现此接口。

如下的代码,是一个经典的代码写法:

public class Base : IDisposable {
  public void Dispose() {
    this.Dispose(true);
    GC.SupressFinalize(this);(对象从Finalizer表去掉)
  }

  protected virtual void Dispose(bool disposing) {
    if (disposing) {
      // 托管类
    }
    // 非托管资源释放
  }

  ~Base() {
    this.Dispose(false);//为什么提供析构函数,是因为避免忘记手动调用Dispose方法。为什么不使用GC.SupressFinalize(this)呢?因为调用析构函数的时候,就已经把对象从Finalizer表去掉了,因为只有对象没有被任何引用的时候,才会调用析构函数的嘛。。
  }
}


总结
一个对象只当在没有任何引用的情况下才会被回收。
一个对象的内存不是马上释放的,GC会在任何时候将其回收。
一般情况下不要强制回收工作。
如果没有特殊的需要不要写Finalizer。
不要在Finalizer中写一些有时间逻辑的代码。
在任何有非托管资源或含有Dispose的成员的类中实现IDisposable接口。
当用胖对象时可以考虑弱引用的使用。