.NET 在非构造对象上调用终结器。

huangapple go评论89阅读模式
英文:

.NET Calls Finalizers for non-constructed objects

问题

我可以翻译以下部分内容:

  1. How correct is this behavior?
    这种行为有多正确?

  2. Is there a description of this mechanism somewhere in the documentation?
    文档中是否有关于这个机制的描述?

  3. Why is this happening?
    为什么会发生这种情况?

Output:
输出:

So we can see there is an object with hash 7880838 has been destroyed, but not constructed.
因此,我们可以看到具有哈希值 7880838 的对象已被销毁,但未被构造。

英文:

I'm playing around with finalizers in c# and found this example which shows that the finalizer can be called even if the constructor didn't succeed.

  1. How correct is this behavior?
  2. Is there a description of this mechanism somewhere in the documentation?
  3. Why is this happening?
internal class Program
{
    public static void Main(string[] args)
    {
        Do();

        GC.Collect();
        Console.ReadLine();
    }

    public static void Do()
    {
        var objects = new BigObject[1000];
        try
        {
            for (var i = 0; i < objects.Length; i++)
            {
                var obj = new BigObject();
                objects[i] = obj;
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
}

public class BigObject
{
    public BigObject()
    {
        Console.WriteLine(GetHashCode());
    }

    private Array _array = new int[1_000_000_000];

    ~BigObject()
    {
        Console.WriteLine("~" + GetHashCode());
    }
}

Output:

58225482
54267293
18643596
33574638
33736294
35191196
48285313
31914638
18796293
34948909
46104728
12289376
43495525
55915408
33476626
System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
   at ConsoleApp11.BigObject..ctor() in C:\Users\t.lemeshko\source\repos\ConsoleApp11\Program.cs:line 41
   at ConsoleApp11.Program.Do() in C:\Users\t.lemeshko\source\repos\ConsoleApp11\Program.cs:line 22
~55915408
~43495525
~12289376
~7880838
~46104728
~34948909
~18796293
~31914638
~48285313
~35191196
~33736294
~33574638
~18643596
~54267293
~58225482
~33476626

So whe can see there is an object with hash 7880838 has been destroyed, but not cinstructed

答案1

得分: 1

文档中:

> 终结器(历史上称为析构函数)用于在类实例被垃圾收集器收集时执行任何必要的最终清理工作。

请注意,对象的创建是一个多步骤的过程,简化来说,它涉及为对象分配内存,然后初始化它。在这种情况下,当它尝试分配数组时,对象的初始化会失败。请注意,数组与BigObject是分开的。你的BigObject实际上非常小,它只包含一个引用。

由于GC已成功为对象分配了内存,它需要清理它,无论初始化是否成功。否则,在构造函数中的任何异常都将导致内存泄漏。并且当对象被收集时,它的终结器需要根据引用的规则运行。

如果失败发生在对象的实际分配步骤中,我不会指望终结器运行。因为没有分配,所以不需要任何清理,因此也没有终结器。如果你的BigObject实际上很大,即包含成千上万的字段而不只是一个。

还要注意,终结器旨在处理非托管资源的释放,在终结器中你不能安全地访问任何其他托管对象,因为它们可能已经被收集。在编写终结器时,你需要总体上小心,因为错误很容易导致内存泄漏或其他错误。

英文:

From the documentation

> Finalizers (historically referred to as destructors) are used to perform any necessary final clean-up when a class instance is being collected by the garbage collector

Note that object creation is a multi step process, simplified this involve allocating memory for the object, and then initializing it. In this case it is the object initialization that fails when it tries to allocate the array. Note that the array is separate from the BigObject. Your BigObject is actually really small, it just contains a single reference.

Since the GC has succeeded in allocating memory for the object it needs to clean it up, regardless if the initialization succeeded or not. Otherwise any exception in a constructor would result in a memory leak. And when the object is collected its finalizer need to run according to the quoted rule.

If the failure occurred in the actual allocation step for your object I would not expect the finalizer to run. Since the there was no allocation there is no need for any cleanup, and therefore no finalizer. This could happen if your BigObject actually was big, i.e. containing millions of fields instead of just one.

Also not that the finalizer is intended for disposing unmanaged resources, you cannot safely access any other managed object within a finalizer, since they may already have been collected. You need to be careful overall when writing finalizers, since mistakes can easily result in memory leaks or other bugs.

答案2

得分: 1

根据这篇文章,字段在调用构造函数之前进行初始化。在这种情况下,异常在构造函数代码执行之前抛出,仍然会创建一个对象,因此对象的终结器在之后被调用。

英文:

According to this article the fields are initialized before calling constructor. In this case the exception is thrown before constructor code execution, still an object is created, hence the object finalizer is called after.

huangapple
  • 本文由 发表于 2023年6月8日 18:22:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76430875.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定