哪些对象可我终结方法使用? [英] Which objects can I use in a finalizer method?

查看:123
本文介绍了哪些对象可我终结方法使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一类处置,或者最终确定后,应该删除一些文件。内部终结我不能用其他的对象,因为他们可能已经被垃圾收集。

我缺少可以用于有关终结和字符串某些时候?

UPD:类似的东西:

 公共类TempFileStream:的FileStream
{
    私人字符串_filename;

    公共TempFileStream(字符串文件名)
        :基地(文件名,FileMode.Open,FileAccess.Read,FileShare.Read)
    {
        _filename =文件名;
    }

    保护覆盖无效的Dispose(BOOL处置)
    {
        base.Dispose(处置);
        如果(_filename == NULL)回报;

        尝试
        {
            File.Delete(_filename); //<  - 哎呀! _filename可能是GC-ED已经
            _filename = NULL;
        }
        赶上(例外五)
        {
            ...
        }
    }
}
 

解决方案

是的,你是绝对可以使用来自一个终结中的字符串,以及许多其他对象类型。

有关这一切的最终来源,我会去拿起通过C#的书 CLR,第三版 ,写的杰弗里里希特。在第21章中对此有详细描述的。

总之,这里的究竟发生什么事......

在垃圾收集,有一个终结仍然希望被称为被放置在一个特殊列表中的任何对象,被称为的 freachable 列表中。

这名单被认为是根,就像静态变量和住局部变量。因此,任何物体的那些的对象是指,等等递归从垃圾收集周期此时除去。 他们度过目前的垃圾收集周期,虽然他们没有资格领取的开始。

请注意,这包括字符串,这是你的问题,但它也涉及到的所有的其他对象类型

然后,在之后的某个时间点,终结器线程拿起从列表中选择对象,并运行对这些对象的终结,然后取这些对象关闭该列表。

然后,将下一次垃圾收集运行时,它发现相同的对象一次,但此时的终结不再想要运行,它已经被执行,所以对象的收集为正常。

让我用一个例子来说明之前,我告诉你什么是行不通的。

让我们假设你有对象的A到Z,每个对象引用下一个,所以你有对象A引用对象B,B引用C,C引用研发,依此类推,直到ž。

一些对象执行终结,他们都实现IDisposable。让我们假设一个没有实现一个终结,但是B呢,然后一些,其余的也还有,它并不适合这个例子确实超出了A和B重要的。

您的程序保存到一个引用,而只有A。

在一个普通的,和正确的,使用模式,你会配置一个,这将处理的B,这将出售C等,但你有一个错误,所以这不会发生。在某些时候,所有这些对象都符合回收。

此时GC会发现所有这些对象,但后来发现,B有一个终结,它尚未运行。因此,GC将放在B上的 freachable 列表中,并递归取C,D,E等多达Z,关闭GC名单,因为由于B突然变成的中 - 符合回收,所以没有休息。请注意,其中一些对象也放置在 freachable 列表本身,因为他们对自己的终结,但是的所有的他们指的对象将生存GC。

有一个,但是,被收集。

让我做上面的段落清晰。在这一点上,A已被收集,但B,C,D等达到Z的仍然活着,好像什么都没发生过的。虽然的的code不再引用了其中任何一个,在 freachable 列表中了。

然后,终结线程运行,并最终完成所有对象在 freachable 列表,并采取关闭的对象列表。

下一次GC运行时,这些对象现在统一收集。

这样肯定的作品,究竟什么是对大bruaha?

现在的问题是与终结器线程。这个线程不会做出有关的顺序应该完成这些对象的假设。它不这样做,因为在许多情况下,这将是不可能的它这样做。

正如我上面所说的,你会在一个普通的世界调用Dispose A,其部署B,其中部署C等。如果这些对象之一是流,引用流对象可能在其调用Dispose ,说:我只是继续和处置流之前刷新我的缓冲区。这是完全合法的和大量的现有code做到这一点。

然而,在最后确定的线程,这个顺序已不再使用,因而如果流被放置参照该对象之前就行了,该流被最终确定,并且因此封闭,对象引用之前。

在换句话说,你不能做的是总结如下:

  

您不能访问任何对象,你的对象是指,具有终结,因为你不能保证这些对象将处于可用状态的终结运行时。所述的物体的仍将的的,在内存中,而不是收集,但它们可能被关闭,终止,最后确定,等等已经

因此​​,回到你的问题的:

  

Q值。我可以在释放方法使用字符串?
  答:是的,因为字符串没有实现一个终结,并且不依赖于有一个终结的其他对象,从而将活蹦乱跳的在您的终结器运行的时间。

这让你走错误的道路是qustion第二句的假设:

  

在终结​​我不能用其他的对象,因为它们可能是垃圾收集了。

正确的一句是:

  

在终结​​我不能使用有终结的其他对象,因为他们已经被敲定。


有关的东西一个例子终结就没有办法知道的顺序来的的正确的完成两个对象,考虑相互引用,并且两者都有终结的两个对象。终结器线程必须分析code,以确定以何种顺序它们通常会被处理,这可能是两个对象之间的舞蹈。终结器线程并没有这样做,它只是最后确定前一个其他的,你有没有保证这是第一个。


那么,有没有时间,它是的安全的访问也有一个终结的对象,从我自己的终结?

唯一可以保证安全的情况是,当你的程序/类库/源$ C ​​$ C拥有两个对象,让你知道它是什么。

在我解释这一点,这是不是真的良好的编程习惯,所以你可能不应该这样做

例如:

您有一个对象,缓存,将数据写入一个文件,这个文件是永远保持开放,因此只有当对象需要将数据写入开吧。

您有另一个对象,的CacheManager ,使用第一个,并调用第一个对象给它的数据写入到文件中。

的CacheManager 有一个终结。这里的语义是,如果管理器类被收集,但不设置,应该删除的高速缓存,因为它不能保证他们的状态

但是,高速缓存对象的文件名是从缓存对象的属性检索。

所以,问题是,我需要做的文件名复制到管理器对象,以避免最后确定过程中出现问题?

不,你不知道。当经理最终确定,缓存对象仍然在内存中,因为它是指文件名字符串。你不能保证什么,但是,是缓存对象的任何终结尚未运行。

然而,在这种情况下,如果您知道的,该高速缓存对象的终结或者不存在,或不接触文件,经理可以读取缓存的文件名属性对象,并删除该文件。

不过,既然你现在有一个pretty的奇怪的依赖怎么回事,我肯定会反对的意见。

I have a class that should delete some file when disposed or finalized. Inside finalizers I can't use other objects because they could have been garbage-collected already.

Am I missing some point regarding finalizers and strings could be used?

UPD: Something like that:

public class TempFileStream : FileStream
{
    private string _filename;

    public TempFileStream(string filename)
        :base(filename, FileMode.Open, FileAccess.Read, FileShare.Read)
    {
        _filename = filename;
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if (_filename == null) return;

        try
        {
            File.Delete(_filename); // <-- oops! _filename could be gc-ed already
            _filename = null;
        }
        catch (Exception e)
        {
            ...
        }
    }
}

解决方案

Yes, you can most certainly use strings from within a finalizer, and many other object types.

For the definitive source of all this, I would go pick up the book CLR via C#, 3rd edition, written by Jeffrey Richter. In chapter 21 this is all described in detail.

Anyway, here's what is really happening...

During garbage collection, any objects that have a finalizer that still wants to be called are placed on a special list, called the freachable list.

This list is considered a root, just as static variables and live local variables are. Therefore, any objects those objects refer to, and so on recursively is removed from the garbage collection cycle this time. They will survive the current garbage collection cycle as though they weren't eligible to collect to begin with.

Note that this includes strings, which was your question, but it also involves all other object types

Then, at some later point in time, the finalizer thread picks up the object from that list, and runs the finalizer on those objects, and then takes those objects off that list.

Then, the next time garbage collection runs, it finds the same objects once more, but this time the finalizer no longer wants to run, it has already been executed, and so the objects are collected as normal.

Let me illustrate with an example before I tell you what doesn't work.

Let's say you have objects A through Z, and each object references the next one, so you have object A referencing object B, B references C, C references D, and so on until Z.

Some of these objects implement finalizers, and they all implement IDisposable. Let's assume that A does not implement a finalizer but B does, and then some of the rest does as well, it's not important for this example which does beyond A and B.

Your program holds onto a reference to A, and only A.

In an ordinary, and correct, usage pattern you would dispose of A, which would dispose of B, which would dispose of C, etc. but you have a bug, so this doesn't happen. At some point, all of these objects are eligible for collection.

At this point GC will find all of these objects, but then notice that B has a finalizer, and it has not yet run. GC will therefore put B on the freachable list, and recursively take C, D, E, etc. up to Z, off of the GC list, because since B suddenly became in- eligible for collection, so does the rest. Note that some of these objects are also placed on the freachable list themselves, because they have finalizers on their own, but all the objects they refer to will survive GC.

A, however, is collected.

Let me make the above paragraph clear. At this point, A has been collected, but B, C, D, etc. up to Z are still alive as though nothing has happened. Though your code no longer has a reference to any of them, the freachable list has.

Then, the finalizer thread runs, and finalizes all of the objects in the freachable list, and takes the objects off of the list.

The next time GC is run, those objects are now collected.

So that certainly works, so what is the big bruaha about?

The problem is with the finalizer thread. This thread makes no assumptions about the order in which it should finalize those objects. It doesn't do this because in many cases it would be impossible for it to do so.

As I said above, in an ordinary world you would call dispose on A, which disposes B, which disposes C, etc. If one of these objects is a stream, the object referencing the stream might, in its call to Dispose, say "I'll just go ahead and flush my buffers before disposing the stream." This is perfectly legal and lots of existing code do this.

However, in the finalization thread, this order is no longer used, and thus if the stream was placed on the list before the objects that referenced it, the stream is finalized, and thus closed, before the object referencing it.

In other words, what you cannot do is summarized as follows:

You can not access any objects your object refer to, that has finalizers, as you have no guarantee that these objects will be in a usable state when your finalizer runs. The objects will still be there, in memory, and not collected, but they may be closed, terminated, finalized, etc. already.

So, back to your question:

Q. Can I use strings in finalizer method?
A. Yes, because strings do not implement a finalizer, and does not rely on other objects that has a finalizer, and will thus be alive and kicking at the time your finalizer runs.

The assumption that made you take the wrong path is the second sentence of the qustion:

Inside finalizers I can't use other objects because they could have been garbage-collected already.

The correct sentence would be:

Inside finalizer I can't use other objects that have finalizers, because they could have been finalized already.


For an example of something the finalizer would have no way of knowing the order in which to correctly finalize two objects, consider two objects that refer to each other and that both have finalizers. The finalizer thread would have to analyze the code to determine in which order they would normally be disposed, which might be a "dance" between the two objects. The finalizer thread does not do this, it just finalizes one before the other, and you have no guarantee which is first.


So, is there any time it is safe to access objects that also have a finalizer, from my own finalizer?

The only guaranteed safe scenario is when your program/class library/source code owns both objects so that you know that it is.

Before I explain this, this is not really good programming practices, so you probably shouldn't do it.

Example:

You have an object, Cache, that writes data to a file, this file is never kept open, and is thus only open when the object needs to write data to it.

You have another object, CacheManager, that uses the first one, and calls into the first object to give it data to write to the file.

CacheManager has a finalizer. The semantics here is that if the manager class is collected, but not disposed, it should delete the caches as it cannot guarantee their state.

However, the filename of the cache object is retrievable from a property of the cache object.

So the question is, do I need to make a copy of that filename into the manager object, to avoid problems during finalization?

Nope, you don't. When the manager is finalized, the cache object is still in memory, as is the filename string it refers to. What you cannot guarantee, however, is that any finalizer on the cache object hasn't already run.

However, in this case, if you know that the finalizer of the cache object either doesn't exist, or doesn't touch the file, your manager can read the filename property of the cache object, and delete the file.

However, since you now have a pretty strange dependency going on here, I would certainly advice against it.

这篇关于哪些对象可我终结方法使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆