各位大虾些!
帮忙看看。以下代码在执行完成后,所占用内存是否会被释放?
        
        private void button1_Click(object sender, EventArgs e) {
            this.Test();
            GC.Collect();
        }        private void Test() {
            //数据类
            ClassC objC = new ClassC();             //向数据类填充数据
            objC.InitC();            //销毁数据类里使用的对象
            objC.Dispose();
            objC = null;
        }探讨一下……

解决方案 »

  1.   

    不会参阅 GC,通往自由的大道
    http://blog.csdn.net/gisfarmer/archive/2009/02/24/3932444.aspx部分拷贝:摘要  
    本文将讲述.NET中的内存管理,GC机制,内存释放过程,各种内存释放方法等,并利用大量示例讲述如何操作并优化回收,另外也讨论一些有关的注意事项。--------------------------------------------------------------------------------目录
    引言 
    自动内存管理和GC 
    GC工作方式 
    Destructor的没落,Finalizer的诞生 
    对象的复活 
    非托管资源的释放 
    弱引用的使用 
    总结 
    参考信息 
    关于作者 --------------------------------------------------------------------------------引言
    作为一个.NET程序员,我们知道托管代码的内存管理是自动的。.NET可以保证我们的托管程序在结束时全部释放,这为我们编程人员省去了不少麻烦,我们可以连想都不想怎么去管理内存,反正.NET自己会保证一切。好吧,有道理,有一定的道理。问题是,当我们用到非托管资源时.NET就不能自动管理了。这是因为非托管代码不受CLR(Common Language Runtime)控制,超出CLR的管理范围。那么如何处理这些非托管资源呢,.NET又是如何管理并释放托管资源的呢?--------------------------------------------------------------------------------自动内存管理和GC
    在原始程序中堆的内存分配是这样的:找到第一个有足够空间的内存地址(没被占用的),然后将该内存分配。当程序不再需要此内存中的信息时程序员需要手动将此内存释放。堆的内存是公用的,也就是说所有进程都有可能覆盖另一进程的内存内容,这就是为什么很多设计不当的程序甚至会让操作系统本身都down掉。我们有时碰到的程序莫名其妙的死掉了(随机现象),也是因为内存管理不当引起的(可能由于本身程序的内存问题或是外来程序造成的)。另一个常见的实例就是大家经常看到的游戏的Trainer,他们通过直接修改游戏的内存达到"无敌"的效果。明白了这些我们可以想象如果内存地址被用混乱了的话会多么危险,我们也可以想象为什么C++程序员(某些)一提起指针就头疼的原因了。另外,如果程序中的内存不被程序员手动释放的话那么这个内存就不会被重新分配,直到电脑重起为止,也就是我们所说的内存泄漏。所说的这些是在非托管代码中,CLR通过AppDomain实现代码间的隔离避免了这些内存管理问题,也就是说一个AppDomain在一般情况下不能读/写另一AppDomain的内存。托管内存释放就由GC(Garbage Collector)来负责。我们要进一步讲述的就是这个GC,但是在这之前要先讲一下托管代码中内存的分配,托管堆中内存的分配是顺序的,也就是说一个挨着一个的分配。这样内存分配的速度就要比原始程序高,但是高出的速度会被GC找回去。为什么?看过GC的工作方式后你就会知道答案了。--------------------------------------------------------------------------------GC工作方式
    首先我们要知道托管代码中的对象什么时候回收我们管不了(除非用GC.Collect强迫GC回收,这不推荐,后面会说明为什么)。GC会在它"高兴"的时候执行一次回收(这有许多原因,比如内存不够用时。这样做是为了提高内存分配、回收的效率)。那么如果我们用Destructor呢?同样不行,因为.NET中Destructor的概念已经不存在了,它变成了Finalizer,这会在后面讲到。目前请记住一个对象只有在没有任何引用的情况下才能够被回收。为了说明这一点请看下面这一段代码:
    [C#]
    object objA = new object();
    object objB = objA;
    objA = null;
    // 强迫回收。
    GC.Collect();
    objB.ToString(); [Visual Basic]
    Dim objA As New Object()
    Dim objB As Object = objA
    objA = Nothing
    ' 强迫回收。 
    GC.Collect()
    objB.ToString() 
    这里objA引用的对象并没有被回收,因为这个对象还有另一个引用,ObjB。
    对象在没有任何引用后就有条件被回收了。当GC回收时,它会做以下几步:
    确定对象没有任何引用。 
    检查对象是否在Finalizer表上有记录。 
    如果在Finalizer表上有记录,那么将记录移到另外的一张表上,在这里我们叫它Finalizer2。 
    如果不在Finalizer2表上有记录,那么释放内存。 
    在Finalizer2表上的对象的Finalizer会在另外一个low priority的线程上执行后从表上删除。当对象被创建时GC会检查对象是否有Finalizer,如果有就会在Finalizer表中添加纪录。我们这里所说的记录其实就是指针。如果仔细看这几个步骤,我们就会发现有Finalizer的对象第一次不会被回收,也就是,有Finalizer的对象要一次以上的Collect操作才会被回收,这样就要慢一步,所以作者推荐除非是绝对需要不要创建Finalizer。为了证明GC确实这么工作而不是作者胡说,我们将在对象的复活一章中给出一个示例,眼见为实,耳听为虚嘛!^_^
    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的原因(除非你也像我一样,写一些有关GC的示例!^_^)。--------------------------------------------------------------------------------
      

  2.   

    我喜欢讨论!
    发表一下我的看法:
     当一个内存对象不在和任何引用联系的时候,GC就可以回收该对象占用的内存了。
     所以  objC = null; 以后 再启动 GC.Collect();
     我觉得时可以回收内存的。
      

  3.   

    我强制启动了!
    “GC.Collect();”
      

  4.   

    呵呵,正好我的BLOG上有关垃圾回收的内容。
    或许对你有帮助。
      

  5.   

    还是不会保证回收GC.Collect()方法尝试回收所有无法访问的内存。然而,Collect 方法并不保证可以回收所有无法访问的内存。无论对象在内存中的时间有多长,所有的对象均被考虑回收;不过,在托管代码中引用的对象不会被回收。使用此方法强制系统尝试回收最大可用内存量。
      

  6.   

    hehe,内存不是怎么好管理的,想管理内存还是搞C++,DELPHI
      

  7.   


    装了个对象。
    但是在  objC.Dispose(); 里将使用的对象删除了。
      

  8.   

    我的意思是如果在InitC()中增加了objC的引用导致还有别的对象依赖他,那他就不会被释放。你可以在ClassC的Finalizer()中输出一些信息看它是否真的被GC回收了
      

  9.   

    一般是不会。
    使用此方法尝试回收无法访问的所有内存。无论对象在内存中的时间有多长,所有的对象均被考虑回收;不过,在托管代码中引用的对象不会被回收。使用此方法强制系统尝试回收最大可用内存量。
    只是尝试而已。Dispose就让objC从终结器列表中移除,少了一次GC周期。
      

  10.   

    鉴于大家对ClassC感兴趣:using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Collections;namespace WindowsFormsApplication1
    {
        public class ClassC
        {
            //表示对象是否已被删除,测试对象是否已经释放,确保不多次删除成员变量   
            private bool isDisposed = false;
            private ClassA objA;        public void InitC() {
                objA = new ClassA();            ArrayList XX = new ArrayList();
                for (int i = 0 ; i < 10000; i++) {
                    XX.Add(new ClassB());
                }
                objA.ArrayObj = XX;
            }        public void Dispose()   
            {   
                Dispose(true);   
                //不需要调用其析构函数   
                GC.SuppressFinalize(this);   
            }   
      
            public virtual void Dispose(bool disposing)   
            {   
                if (!isDisposed)   
                {   
                    if (disposing)   
                    {
                        objA = null;
                    }
                    objA = null;
                    //清理未托管对象   
                }   
                isDisposed = true;   
            }   
      
            //析构函数   
            ~ClassC()
            {   
                Dispose(false);   
            }   
      
            public void SomeMothod()   
            {   
                if (isDisposed)   
                {
                    throw new ObjectDisposedException("ClassC");   
                }   
            }   
        }
    }
      

  11.   

    补充:
     public class ClassC :IDisposable
      

  12.   

    可是实现IDisposable接口IDisposable说明
    此接口的主要用途是释放非托管资源。当不再使用托管对象时,垃圾回收器会自动释放分配给该对象的内存。但无法预测进行垃圾回收的时间。另外,垃圾回收器对窗口句柄或打开的文件和流等非托管资源一无所知。MSDN的一个正确使用的例子
    http://msdn.microsoft.com/zh-cn/library/ms244737%28VS.80%29.aspx楼主给分
      

  13.   

    再没有其他对象引用objC了……
      

  14.   


    看了一下MSDN:
    1、实现IDisposable接口只是为了提醒使用者该类的对象需要调用Dispose来释放非托管资源;
    2、至于托管内存我们是没有办法直接插手管理的,顶多只能是提醒一下系统现在需要强制回收内存:
    GC.Collect();
    但是这仅仅用来告知系统进行回收,至于系统怎样具体的执行回收动作可以参考1楼的内容;
    所以结果就是:
    由于具体的回收动作和回收规则是由.Net系统管理的,所以在我们调用
    GC.Collect();后不能确保某个对象一定会被回收(仅仅是有可能)。
      

  15.   


    呵呵 LIKE YOU ……我再发表点我的看法:
    对于:GC.Collect();
         MSDN 曰: 使用此方法尝试回收无法访问的所有内存。 
                    无论对象在内存中的时间有多长,所有的对象均被考虑回收;不过,在托管代码中引用的对象不会被回收。使用此方法强制系统尝试回收最大可用内存量。 
         所谓尝试:是说,如果heap中某某内存区块还被stack中某对象指向,那么该该内存空间不被释放。
    那么对于上述objC,已经被重置为NULL了,也就是说它在stack上的指针被清除了,那么原先为objC在heap上分配的内存空间,在这个时候启动垃圾回收器是应该能被回收的。继续讨论……
     
      

  16.   


    呵呵 LIKE YOU ……我再发表点我的看法:
    对于:GC.Collect();
         MSDN 曰: 使用此方法尝试回收无法访问的所有内存。 
                    无论对象在内存中的时间有多长,所有的对象均被考虑回收;不过,在托管代码中引用的对象不会被回收。使用此方法强制系统尝试回收最大可用内存量。 
         所谓尝试:是说,如果heap中某某内存区块还被stack中某对象指向,那么该该内存空间不被释放。
    那么对于上述objC,已经被重置为NULL了,也就是说它在stack上的指针被清除了,那么原先为objC在heap上分配的内存空间,在这个时候启动垃圾回收器是应该能被回收的。继续讨论……
     
      

  17.   

    嗯,“是应该能被回收的”确实是这样;
    不过你也知道仅仅是应该能被回收的,由于GC如果在Generation 0中回收了足够的内存,那么GC就不会再接着回收了;
    所以仅仅是有可能。
      

  18.   


    class Program
        {
            private const int maxGarbage = 1000;        static void Main(string[] args) {
                //这个时候程序内存占用量是4360k            // Put some objects in memory.
                Program.MakeSomeGarbage();
                Console.WriteLine("Memory used before collection: {0}", GC.GetTotalMemory(false));
                //这个时候程序内存占用量是4552k            // Collect all generations of memory.
                GC.Collect();
                Console.WriteLine("Memory used after full collection: {0}", GC.GetTotalMemory(true));
                //这个时候程序内存占用量是4660k
            }        static void MakeSomeGarbage() {
                Version vt;            for (int i = 0 ; i < maxGarbage ; i++) {
                    // Create objects and release them to fill up memory
                    // with unused objects.
                    vt = new Version();
                }
            }
        }
    好玩……
      

  19.   

    鉴于上面控制台输出内容可能会占内存。
    去掉后的版本:class Program
        {
            private const int maxGarbage = 1000;        static void Main(string[] args) {
                //这个时候程序内存占用量是4336k            // Put some objects in memory.
                Program.MakeSomeGarbage();
                //这个时候程序内存占用量是4428k            // Collect all generations of memory.
                GC.Collect();
                //这个时候程序内存占用量是4496k
            }        static void MakeSomeGarbage() {
                Version vt;            for (int i = 0 ; i < maxGarbage ; i++) {
                    // Create objects and release them to fill up memory
                    // with unused objects.
                    vt = new Version();
                }
            }
        }
    还是很奇怪……
      

  20.   

    看了楼主的代码明白了。在你执行完了GC.Collect()之后,objC占用的内存肯定会被释放掉,但是不会执行ClassC的Finalizer(),因为你在Dispose()中调用了 GC.SuppressFinalize(this);
      

  21.   

    一般调用GC.Collect()后还要调用GC.WaitForPendingFinalizers(),因为GC是在另外的线程中做的。你跑的结果中Collect()后内存没有减少可能是机器的原因,在我的机器上(P4),Collect()后结果少了近一半。
      

  22.   

    一般调用GC.Collect()后还要调用GC.WaitForPendingFinalizers(),因为GC是在另外的线程中做的。你跑的结果中Collect()后内存没有减少可能是机器的原因,在我的机器上(P4),Collect()后结果少了近一半。呵呵!真的!不行!
    我重新测试了(加了WaitForPendingFinalizers )。
    发现:不仅本程序内存使用量没有变化,整个机器的也没有变化。神了……
      

  23.   

    GC不会马上释放。而且使用GC也会出现一些DISPOSE的问题。最好的方法是你重写DISPOSE方法。在方法中分别释放对象。C#对内存的操作不是很灵活。
      

  24.   

    楼主无视沙发了?再次张贴,请参阅
    http://blog.csdn.net/gisfarmer/archive/2009/02/24/3932444.aspx看不懂的再来讨论吧 
      

  25.   

    说的不错。微软推荐试用Dispose方法来做。具体可以参见msdn