鲜荣彬
Herry

     我们在编写程序的时候会用到各种不同的资源,比如内存块,屏幕区域,文件,网络连接,数据源等等。 我们必须对这些资源进行有效的操作,才能确保我们程序快速而高效的运行。

      一般来说,我们会对资源如此的操作,为对应的资源分配内存————》初始化内存----》使用资源----》清理资源------》释放内存,这是我们最常用的方法。学过C++的朋友们应该知道,C++中的指针是另程序员很头疼的一件事,因为指针是直接指向内存,如果对指针处理不当,必将让程序效率大打折扣的(当然,我学过,比如MFC)。如果你只知道基础的语法知识,那么那你是没那么深得体会的。C++程序员会手动管理内存,当new了一个对象后,会在这个类的析构函数中释放这个对象所占用的内存。 当然你可以明确不要这个资源,但会经常出错, 后果很严重,不仅内存泄露,而且数据内容一团槽,而大部分的时候 程序的行为会变的不可预测。

    
     当然Java 与.net的程序员会十分的幸福,因为都存在自动内存管理这样一个机制,程序员不必当心内存的问题,GC 自动帮助我们释放内存,在书写应用程序的时候。下面我就谈谈我对GC的一些了解,帮助我和大家一起深刻了解这个问题!
     在将GC的时候,必须引入代这个概念。代(Generation)提高性能,否则会收集整个的Heap,可以猜想性能是多么的差!
     垃圾收集器将对象分为三代
     G0 小对象  大小为<512 kb ,当然也不是固定的,可以自动调整。
     G1 在GC中幸存下来的G0的对象,一般为2MB,也可以自动调整。
     G2 大对象 大小一般大于10MB,是在 在GC中幸存下来的G1对象,也可以自动调整。
     如下代码,可以说明这点,GC中确实只有三代

class Program
{
static void Main(string[] args)
{
Console.WriteLine("The highest generation is {0}",GC.MaxGeneration);
SmallObject smallObjet=new SmallObject();
Console.WriteLine("First(NO GC): Generation: {0}",GC.GetGeneration(smallObjet));
GC.Collect();

Console.WriteLine("Second(GC Once): Generation:{0}",GC.GetGeneration(smallObjet));

GC.Collect();

Console.WriteLine("Third (GC Twice): Generation {0}",GC.GetGeneration(smallObjet));

//int[] largeObject = new int[21250];
Console.Read();

}
}
class SmallObject
{
int i, j, k;
}

                运行结果如下图

                                          


    代收集的规则是 当一个代N被收集以后 在这个代里幸存下来的对象会被标记为N+1对象。
    那么GC中是如何找到为被引用的对象,而将其内存释放的呢?微软工程师大致的想法是这样的,先找到所有的GC的根结点(GC Root), 将他们放到队列里,然后依次递归地遍历所有的根结点以及引用的所有子节点和子子节点,将所有被遍历到的结点标记成live。弱引用不会被考虑在内。  

 GC的工作流程如下图

    

                   

    下面这个代码,你可以自己看看,

   

 1 class Base
2 {
3 public string name;
4 public Base(string iName) { name = iName; }
5 ~ Base()
6 {
7 Console.WriteLine("object "+name+" is GCed");
8 }
9 }
10 class A : Base
11 {
12 public A(string iName) : base(iName) { }
13 public B b;
14 }
15 class B : Base
16 {
17 public B(string iName) : base(iName) { }
18 public C c;
19 }
20 class C : Base
21 {
22 public C(string iName) : base(iName) { }
23 public D d;
24 }
25 class D : Base
26 {
27 public D(string iName) : base(iName) { }
28 public B b;
29 }
30 class Program
31 {
32 static A a = null;
33 static void Main(string[] args)
34 {

36 A localA = new A("loacalA");

38 a = new A("a1");
39 B localB = new B("B1");
40 C localC = new C("C1");
41 D localD = new D("d1");
42
43 a.b = localB;
44 localB.c = localC;
45 localC.d = localD;
46 localD.b = localB;
47
48 localA.b = localB;

50 Console.WriteLine("Thread Main:Start GC....");
51 GC.Collect();
52 GC.WaitForPendingFinalizers();
53 Console.WriteLine("Thread Main: Finsish GC....");

55 Thread ta = new Thread(new ThreadStart(Run));
56 Console.WriteLine("Thread Main:Start a new Thread:TA");
57 ta.Start();

60 Console.WriteLine("Thread Main:Sleep to wait for ta exist");
61 Thread.Sleep(10000);
62 Console.WriteLine("Thread Main:exit");
63 Console.Read();
65 }
66 public static void Run()
67 {
68 a = new A("a2");
69 Console.WriteLine("Thread TA:Reassign a new object to a");
70 Console.WriteLine("Thread TA:Start GC....");
71 GC.Collect();
72 GC.WaitForPendingFinalizers();
73 Console.WriteLine("Thread TA:Finish GC....");
74
75 Thread.Sleep(5000);
76 Console.WriteLine("Thread TA:Exit...");
77 }
78 }

  运行结果如下:

    

                                   

上面这个代码可能有些问题,希望懂得这方面的人可以帮我解惑!按照我的思路是 Object locaclA is GGed 应该出现在第二句话之前,Object a1 is GGed应该出现在倒数第二的位置上,但却没有,这让我很伤心啊!求各位大侠解惑!

   

当然,这世界上根本不存在一种算法预测某个变量在将来是不会被用到的,.net 只是在JT 生成CLR库的时候,加上一些变量,看这个对象到这个对象的过程中,某个变量还会不会被另外一个变量所引用到。当然这只是一个大致的思路。
    最后,根据我的经验,尽量不要new很大的Object 不要频繁的new 生命周期很短的Object, 这样会导致很多内存碎片导致频繁的调用GC,也是不好的!

posted on 2011-11-27 17:24  Herry彬  阅读(5268)  评论(0编辑  收藏  举报