Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/264.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .NET垃圾收集器的奇怪行为_C#_Garbage Collection - Fatal编程技术网

C# .NET垃圾收集器的奇怪行为

C# .NET垃圾收集器的奇怪行为,c#,garbage-collection,C#,Garbage Collection,MSDN表示用于GC.Collect 所有对象,无论它们在内存中存在多长时间,都是 考虑收集;但是,在中引用的对象 未收集托管代码。使用此方法强制系统 尝试回收最大可用内存量 因此,我希望在收集父类之前,不会收集仍在父类中引用的子类 但奇怪的是,它大多是在父母被收集之前收集的。这对我来说毫无意义 我在VS2010上编译以下代码,并在Framework4.0上运行它。 我得到的是: 有人能解释一下这里发生了什么事吗 编辑: 我已经知道如何解决这个问题了。如果要访问类的终结器中的类成员,如果这些成员

MSDN表示用于GC.Collect

所有对象,无论它们在内存中存在多长时间,都是 考虑收集;但是,在中引用的对象 未收集托管代码。使用此方法强制系统 尝试回收最大可用内存量

因此,我希望在收集父类之前,不会收集仍在父类中引用的子类

但奇怪的是,它大多是在父母被收集之前收集的。这对我来说毫无意义

我在VS2010上编译以下代码,并在Framework4.0上运行它。 我得到的是:

有人能解释一下这里发生了什么事吗

编辑:

我已经知道如何解决这个问题了。如果要访问类的终结器中的类成员,如果这些成员本身也有终结器,则可能会出现问题。在这种情况下,在类的终结器可以访问成员之前,成员可能已经死亡,因为垃圾收集器会以任何顺序销毁它们。父项之前的子项或父项之后的子项

但是,如果您访问的类成员没有自己的终结器,则不会出现此问题

因此,如果您希望在类中存储句柄列表,并且希望在终结器中关闭这些句柄,那么请确保此列表类没有自己的终结器,否则在关闭它们之前,句柄可能已丢失

因此,我希望在收集父类之前,不会收集仍在父类中引用的子类

这不是一个真实的假设。GC可以自由收集任何对象,只要它能够证明该对象不再可以从将来任何时候运行的任何代码中访问。允许在该点收集任何对象,但可以自由收集或保留满足该条件的任何对象。如果一个对象引用另一个对象,但它们都不是根对象,也不能从任何根对象访问,那么GC可以按任意顺序删除它们,甚至可以删除子对象而不是父对象

同样值得注意的是,您的代码没有显示任何内容。对象的终结器可以在符合收集条件和实际收集之间的任何时间点运行。即使两个终结器都运行,终结器运行的顺序也不能保证对象本身收集的顺序

当然,在实践中,两个对象实际上完全同时被收集的几率非常高,除非其中一个对象的存在时间比另一个对象长得多。GC的运行方式是将给定层中的所有对象视为死对象,然后将那些仍然活动的对象复制到新的节中,使所有未复制的对象在发生需要该内存的情况时被重写,因此,如果两个对象都在同一个GC层中(这很可能),那么两个位置的内存能够在完全相同的时间被覆盖。至于内存何时被覆盖,甚至很难知道它是否被覆盖


因此,最终引用的期望背后的整个概念在许多不同的层面上都不是一个真正合理的前提。

垃圾收集器非常聪明-可能它检测到列表未被使用,所以它会清理。您启用优化了吗?只是为了调试目的,从@ D3C34 C34 D的书中获取一页,并考虑克隆该项目,并用配置/优化进行调整,直到发现可能或可能没有注入行为。收集没有什么奇怪的事情发生。您假设在GC期间,每个对象都被检查、最终确定,然后被收集。然而,这将是非常低效的;相反,GC检查并标记要收集的每个项目,然后完成所有项目,然后收集所有项目。您将完成与收集混淆了。它们根本不是一回事。如果你想了解关于定稿的其他错误,你可能会从我的系列文章中受益。谢谢你的回答。这意味着我不能在终结器中编写访问任何类成员的代码,因为在那一刻,它们可能已经死了——部分或全部死了。我不知道。@Elmue那与我说的完全相反。也就是说,出于许多其他原因,您仍然不应该访问对象的托管属性,但这并不是因为它可能已被收集,因为在终结器完成之前,它实际上无法完全收集。相反,情况如何?你写了。。。GC可以按任意顺序删除它们。。。。这意味着孩子可能比父母早死了。因此,在Parent.Finalize中可能已经无法访问子级。请阅读我的编辑。@Elmue对象可以按任何顺序定稿,并且 它们实际上可以按任何顺序收集,但在父级被最终确定之前无法收集子级,因为可以通过终结器执行托管代码来访问它。再次强调,定稿和收集是两件完全不同的事情,你似乎仍然将它们视为同一件事;他们根本不是。
using System;

namespace GarbageCollector
{
    class Child
    {
        public bool bInUse = true;
        public void Dispose()
        {
            Console.WriteLine("Child finished by Parent.");
            bInUse = false;
        }

        ~Child()
        {
            bInUse = false;
        }
    }

    class Parent
    {
        Child child = new Child();
        ~Parent()
        {
            if (!child.bInUse)
                Console.WriteLine("Finalizing Child that is still in use in a Parent!");

            child.Dispose();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                for (int i=0; i<10; i++)
                {
                     Parent P = new Parent();
                }
                GC.Collect();
            }
        }
    }
}