Effective C# Chapter2-.Net Resource Management

《EffectiveC#》这本书讲了一些关于C#语言的使用技巧和经验. 该系列文章是备忘录和自己的一些见解.程序员们最喜欢这类问题了,欢迎讨论~

第二章主要讲资源管理相关,而GC则是这章的绝对主角。

关于GC,有太多的文章讨论这个机制,如果下列概念不太清晰的话,就意味着需要再去看看MSDN的文档了:

托管堆、分代收集算法、Root对象,引用关系图、Finalizer的调用时机,WeakReferance。

大对象和小对象,对象固定 http://www.cnblogs.com/futao/archive/2009/12/23/1630461.html

这里有一篇关于GC的算法发展的文章 http://blog.csdn.net/KAI3000/article/details/314628


个人对GC的总结:

        C#使用GC作为内存管理的机制。
        现阶段C#使用的是分代收集算法。
        对象一共有3代:第0代,第1代,第2代
        程序对象分配是在第0代和LOH(大对象堆)上进行的。只有GC程序可以在第一代进行垃圾回收。
        LOH一定是第2代对象。
        当程序要分配内存了,而当前的第0代或LOH空间不足,就会触发垃圾回收动作。
        好的程序结构应该是在对第0代进行垃圾收集时,就可以将程序内大部分不使用的对象进行回收。
        如果经过了垃圾回收还不能满足分配需求,就会进行堆扩展。
        垃圾对象的判定标准是:根据”对所有Root对象进行依赖分析“,建立若干依赖关系图,所有不在这些图中的对象就是垃圾对象。
        Root对象是具有强引用的对象,但反过来不成立,具有强引用的对象不一定是Root对象,也可能只是依赖关系中的某一环对象。
        对所有代的垃圾回收都是阻塞主线程的,GC操作的耗时从几十毫秒~几百毫秒,甚至几秒都是有可能的。
        即使对象已经被标记为垃圾对象,对它的回收也可能有两个步骤:直接回收堆内存,或者进入结束队列,等待调用Finalizer。
        Finalizer队列和GC不在同一个线程。
        对象的Finalizer方法和GC毛关系都没有,是处理非托管对象的释放用的。理论上来讲,可以不依赖Finalizer,而是靠良好的编程规范管理好非托管资源。
        第0代和第1代的对象会被GC机制在托管堆上”移动“,为的是腾出更多的连续内存空间。这个压缩操作是在Finalizer队列执行完成之后。(话说谁在意这个时机)
        第2代对象即使被回收了,但是LOH上剩余的对象也不会移动(将来可能会)。
        在第2代的内存空间里,可以认为和C++等价,是会在虚拟内存中产生碎片的。
        如果真关心对象的内存地址,应该将对象固定。
        如果非要手工的调用GC,最好只做第0代的清理操作,频繁的调用对所有代的回收,这会导致大量的小对象进入第2代,一旦进入第2代,内存碎片的问题就越发明显,这会导致堆的尺寸变得巨大。而且手工调用GC也会干扰GC使用的策略引擎工作效率下降--该引擎基于GC的结果动态调整GC调用的阀值和频率。


Item 12 使用Initializers代替Assignment Statements


我觉得可以这样理解:别在构造函数里做对象的赋初值。

1、使用”在构造函数里赋值“的策略,会在增加新成员变量时,发生忘记赋值的可能性大大增加。
class A
{
        public A(){a = 3;} // bad
        public int a = 3; // good
}

因为他俩不同步了呗,隔得又远。而且如果有多个构造函数,更加凸显代码不同步的问题。
2、只有在成员变量初始化时可能发生异常,才使用构造函数。
这个很好理解,成员变量初始化的语法结构不支持try/catch/finaly
class A
{
        public A()
       {
              try
              {
              _file = File.Open("C:/sdja.txt");
              }
              catch
             {
                    // do something to fix it;
             }
       }
        private File _file;
}


Item 13 用静态构造函数来初始化静态变量


1、静态构造函数在任何方法、变量、属性被访问之前执,不论是不是static , readonly, const
这个调用时机很有用,我用这种语法特性实现了一些特殊的解耦和依赖倒转结构。
2、如果只是简单的对成员进行初始化,使用Intializer语法,只有在复杂赋值逻辑或者会产生异常时,才使用static constructor
这个也没啥问题。
3、如果在static constructor 里抛出异常,CLR会立刻终止程序的运行
这个可以拿来快速关闭程序 (^_^)


Item 14 利用构造函数链


1、构造函数链
public MyClass():this(1,2){}
public MyClass(int a, int b){}
2、C#编译器将构造函数视为一种特殊的语法,将所有构造函数中对成员对象的Initializer、以及基类构造函数中对同样成员的Initializer的重复调用移除。
3、可以用this(...) 和 base (...) ,不能同时使用
4、构造对象的工序
    1、静态成员设置为0 (value_type = 0, ref_type = null)
    2、静态成员的Initializer被调用
    3、基类的静态构造函数被调用
    4、静态构造函数被调用
    5、实例成员设置为0 (value_type = 0, ref_type = null)
    6、实例成员的Initializer被调用
    7、基类的构造函数被调用
    8、构造函数被调用
优化在步骤6、7。可以看到,在C#里对象初始化的顺序为:置0->initializer->base.constructor->constructor ,先静态,后实例。


Item 15 利用using 和 try/finally 来清理资源


1、.Net认为非托管资源应由代码负责,而不是资源系统,或者资源类的责任。
2、所有非托管资源类都应该有Dispose方法,该方法的调用责任也是开发人员负责
3、确保Dispose方法一定会被调用的最好方法就是使用 using语法 或者 try/finally 语句块
4、using本质上生成一个try/finally 语句块
5、using语法不支持非IDisposable对象,弱对象不是IDisposable,会抛出异常。
6、using 语法支持如下调用
using(x)
using(y)
{
    //do something
}
但是这种写法有潜在bug:如果构造y的时候发生了异常, x 的Dispose方法永远不会被调用。
7、Dispose方法不代表将一个对象从内存中移除,只是表示这是一个可以将对象所持有的非托管资源释放的方式。


Item 16 尽可能的少进行GC


少new,除了一些显示的、手工写出来的new,还有一些C#隐式的new操作。
1、string 的 + 会产生新字符串
2、boxing和unboxing都会new
3、包含非静态成员变量的闭包会导致new
4、lambda表达式转换成对应Delegate时会有new(这个超隐晦)


Item 17 尽可能少的boxing和unboxing


因为有new..


Item 18 实现标准的Dispose流程


protected bool _disposed = false;
protected virutal _Dispose(bool disposing)
{
    if(_disposed)
        return;

    if( disposing)
    {
        // free managed resource or objects, like set value 0;
    }

    // free unmanaged resource.

    base._Dispose(disposing);

    _disposed = true;
}

~T()
{
   _Dispose(false);
}

void Dispose()
{
    _Dispose(true);
    GC.SuppressFinalize(true);
}

1、避免重复释放
2、dispose被调用表示手工调用释放,所以可以在这时对非托管资源进行释放,并通知GC将自己移除finalizer调用队列。同时此时也对托管对象进行释放,使相关对象更早的被GC标记为垃圾对象。
3、finalizer被调用表示该对象已经被标记为可回收的对象了,其中的非托管对象全部都做过GC的检查,都已经被标记为可回收的了,所以此时只做对非托管对象的回收。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值