当对象超出c#的范围时?

时间:2016-01-30 04:53:56

标签: c# garbage-collection destructor

显式定义范围的方法。

static void Main(string[] args)
{
    Class1 c1 = new Class1(1);

    {
        Class1 c2 = new Class1(2);

        {
            Class1 c3 = new Class1(3);
        }

    //this is not collecting object c3 which is out of scope here
    GC.Collect();
    }

    //this is not collecting object c2 which is out of scope here
    GC.Collect();
    Console.ReadKey();
}

Class1定义:

class Class1
{
    int x;
    public Class1(int a)
    {
        x = a;
    }
    ~Class1()
    {
        Console.WriteLine(x + "object destroy");
    }
}

我写这段代码。但是GC.Collect()方法不会收集超出范围的对象。

3 个答案:

答案 0 :(得分:2)

GC.Collect()并不真正意味着用于确定性的资源回收。 (这就是你的真实代码中的终结器正在做什么,对吗?)

如果您有非托管资源需要处理,请考虑使用Microsoft.Win32.SafeHandles中的某个类。

如果您想要确定性地释放资源,则应该实现IDisposable,并在完成使用该对象后调用Dispose()。这样做的惯用方法是:

using (var someResource = new Class1(1)) 
{
    // work with the `Class1` object
}

在功能上等同于

{
    var someResource = new Class1(1);
    try 
    { 
        // work with the `Class1` object 
    }
    finally
    {
        someResource.Dispose();
    }
}

答案 1 :(得分:0)

您对范围界定的看法是正确的。对象超出了您的预期范围。但是,显然你测试它的方式并不正确。

这里要考虑几点。

  1. 只有当有任何非托管资源时,才应实施终结器。 如果你的类有一个终结器,并且如果你没有以implement IDisposable Correctly的方式抑制终结,那么该类的实例可以驻留在内存中两个垃圾收集周期。因为,如果需要完成,第一个GC.Collect会将该实例添加到终结队列。可能是第二个垃圾收集周期实际上清除了内存。

  2. Finalizer是.net对象可以释放非托管资源的最后一点。 仅当您未正确处理实例时才会执行终结器。理想情况下,决不应该执行终结器。因为适当的处置实施应该抑制最终确定。

  3. 根据您的示例代码,如果您致电GC.Collect and WaitForPendingFinalizers,您的测试可能会通过(不确定如何查找是否已清除内存)。

  4. 更好地测试你。

    清除内存时,垃圾收集器不会考虑内部作用域。如果将代码移动到单独的方法中,则执行终结器。

    测试1

        static void Main(string[] args)
        {
            GCTest();
    
            Console.ReadKey();
        }
    
    
        private static void GCTest()
        {
            Class1 c1 = new Class1(1);
    
            {
                Class1 c2 = new Class1(2);
    
                {
                    Class1 c3 = new Class1(3);
                }
    
                //this is not collecting object c3 which is out of scope here
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
    
            //this is not collecting object c2 which is out of scope here
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    

    测试2

    如果要为垃圾收集目的指定范围,请使用using statement

        private static void Main(string[] args)
        {
            using (Class1 c1 = new Class1(1))
            {
                using (Class1 c2 = new Class1(2))
                {
                    Class1 c3 = new Class1(3);
                }
            }
    
            Console.ReadKey();
        }
    
        class Class1 : IDisposable
        {
            int x;
            public Class1(int a)
            {
                x = a;
            }
    
            public void Dispose()
            {
                Console.WriteLine(x + "object disposing");
            }
        }
    

    输出

      

    2对象处理

         

    1对象处理

答案 2 :(得分:0)

这些范围不会成为IL。运行时不知道他们在那里。在IL级别,只有本地和堆栈内容。虽然.NET JIT一般很差,但它可以很好地将本地和临时值优化成一种有效的形式。

无法保证何时收集对象。 GC.Collect无法强制执行此操作。有一些安全模式可以保证99.999%,但永远不会完全。

你永远不需要这个设施。事实上,拥有终结器的需求几乎为零。使用SafeHandle基础架构。

如果您真的想看到效果,请将所有这些代码移到辅助方法中并从Main调用它。回到主页,Collect将成功。