C#应用程序退出是否会自动处置托管资源? [英] Does C# app exit automatically dispose managed resources?

查看:141
本文介绍了C#应用程序退出是否会自动处置托管资源?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我完全知道,使用语句是处理IDisposable的方法.请不要在评论中重复此建议.

当C#.NET 4.5(或更高版本)应用程序关闭时,未正确处置的IDisposables会发生什么?

我知道有些人可以使用终结器来处理非托管资源.

但是,假设我有一个控制台应用程序,带有静态Stream变量.当我关闭控制台应用程序时会丢弃它吗?

HttpClient呢?以及您如何知道在哪些情况下会发生这种情况?

好的,现在提供一些实际的背景信息.我经常将某些IDisposables存储为字段,从而迫使我的班级实现IDisposable.最终用户应使用using.但是,如果那没有发生怎么办?

在GC之前是否只是不必要的内存?还是突然有内存泄漏?

解决方案

重要的是要区分实现IDisposable的对象和带有终结器的对象.在大多数情况下(可能最好是所有情况),带有终结器的对象也实现IDisposable,但实际上它们是两个截然不同的东西,最常一起使用.

终结器是一种对.NET运行时说的机制,在它可以收集对象之前,它必须执行终结器.当.NET运行时检测到对象符合垃圾收集条件时,就会发生这种情况.通常,如果对象没有终结器,则将在此收集期间收集该对象.如果它具有终结器,则将其放置在列表中,即可访问队列",并且有一个监视该线程的后台线程.有时在集合将对象放入此队列之后,终结器线程将从该队列中处理该对象并调用finalizer方法.

一旦发生这种情况,该对象将再次有资格进行收集,但是它也已被标记为已完成,这意味着当垃圾收集器在以后的收集周期中找到该对象时,它将不再将其放置在此队列中,而是将其放置在队列中.正常收集.

请注意,在以上文本段落中,没有提到IDisposable,这是有充分理由的.以上所有都不依赖于IDisposable .

现在,实现IDisposable的对象可能具有终结器,也可能没有终结器.一般规则是,如果对象本身拥有非托管资源,则它可能应该拥有,如果没有,则不应该拥有. (我很犹豫,总是在这里永远不要说,因为似乎总有人会找到一个可以以某种方式有意义但又打破了典型"规则的拐角处)

上面的 TL; DR 总结可能是终结器是一种在对象被收集时获得(半)保证的清除的方法,但是确切地讲,这种情况并非如此直接在程序员的控制之下,而实现IDisposable是直接从代码控制此清理的一种方式.

无论如何,我们将全力以赴,让我们解决您的特定问题:

当C#.NET 4.5(或更高版本)应用程序关闭时,未正确处置的IDisposables会发生什么?

答案:没什么.如果它们具有终结器,则终结器线程将尝试拾取它们,因为当程序终止时, all 对象将有资格进行收集.但是,不允许终结器线程永远"运行以执行此操作,因此它也可能用完时间.另一方面,如果实现IDisposable的对象没有终结器,则将正常地对其进行收集(再次,IDisposable与垃圾回收完全没有关系).

但是,假设我有一个控制台应用程序,带有静态Stream变量.当我关闭控制台应用程序时会丢弃它吗?

答案:不会,不会被处置. Stream本身是基类,因此根据具体派生类,它可能有也可能没有终结器.但是,它遵循与上述相同的规则,因此,如果没有终结器,则将仅对其进行收集.例如, MemoryStream 没有终结器,而 FileStream 可以.

HttpClient呢?以及您如何知道在哪些情况下会发生这种情况,

答案:

  • 此处最重要的部分是您需要区分 程序执行期间和 程序执行之后

    • 当进程终止时,操作系统将回收分配给它的所有内存,它将关闭所有句柄(这可能会使套接字,文件等保持打开状态),所有线程将终止.简而言之,该程序已从内存中完全删除
    • 该过程可能遗留了一些小窍门,除非该过程事先做好了准备,否则这些小消息将不被清除.如上所述,打开的文件已关闭,但它可能尚未完全写入,因此可能以某种方式被破坏.
    • 在程序执行期间,泄漏可能会使程序在分配的内存方面增长,可能由于无法关闭不再需要的句柄而分配了过多的句柄,等等.这对于处理IDisposable很重要和终结器正确,但是当过程终止时,这不再是问题.
  • I am fully aware that using statements are the way to handle IDisposables. Please do not repeat this advice in comments.

    When a C# .NET 4.5 (or higher) application closes, what happens to the IDisposables which were not properly disposed?

    I know some have a finalizer for disposing unmanaged resources.

    But let's say I have a console app, with a static Stream variable. Is it disposed when I close the console app?

    What about a HttpClient? And how do you know in which situations it does and in which is does not?

    Alright, now some actual background info. I often store certain IDisposables as fields, forcing my class to implement IDisposable. The end user should use using. But what if that does not happen?

    Is it merely unnecessary memory until GC? Or do you suddenly have a memory leak?

    解决方案

    It's important to distinguish between objects implementing IDisposable and objects with finalizers. In most cases (probably preferably all), objects with finalizers also implement IDisposable but they are in fact two distinct things, most often used together.

    A finalizer is a mechanism to say to the .NET Runtime that before it can collect the object, it has to execute the finalizer. This happens when the .NET Runtime detects that an object is eligible for garbage collection. Normally, if the object does not have a finalizer, it will be collected during this collection. If it has a finalizer, it will instead be placed onto a list, the "freachable queue", and there is a background thread that monitors this thread. Sometimes after the collection has placed the object onto this queue, the finalizer thread will process the object from this queue and call the finalizer method.

    Once this has happened, the object is again eligible for collection, but it has also been marked as finalized, which means that when the garbage collector finds the object in a future collection cycle, it no longer places it on this queue but collects it normally.

    Note that in the above paragraphs of text, IDisposable is not mentioned once, and there is a good reason for that. None of the above relies on IDisposable at all.

    Now, objects implementing IDisposable may or may not have a finalizer. The general rule is that if the object itself owns unmanaged resources it probably should and if it doesn't it probably shouldn't. (I'm hesitant to say always and never here since there always seems to be someone that is able to find a cornercase where it makes sense one way or another but breaks the "typically" rule)

    A TL;DR summary of the above could be that a finalizer is a way to get a (semi-)guaranteed cleanup of the object when it is collected, but exactly when that happens is not directly under the programmers control, whereas implementing IDisposable is a way to control this cleanup directly from code.

    Anyway, with all that under our belt, let's tackle your specific questions:

    When a C# .NET 4.5 (or higher) application closes, what happens to the IDisposables which were not properly disposed?

    Answer: Nothing. If they have a finalizer, the finalizer thread will try to pick them up, since when the program terminates, all objects become eligible for collection. The finalizer thread is not allowed to run "forever" to do this, however, so it may also run out of time. If, on the other hand, the object implementing IDisposable does not have a finalizer it will simply be collected normally (again, IDisposable has no bearing at all on garbage collection).

    But let's say I have a console app, with a static Stream variable. Is it disposed when I close the console app?

    Answer: No, it will not be disposed. Stream by itself is a base class, so depending on the concrete derived class it may or may not have a finalizer. It follows the same rule as above, however, so if it doesn't have a finalizer it will simply be collected. Examples, MemoryStream does not have a finalizer, whereas FileStream does.

    What about a HttpClient? And how do you know in which situations it does and in which is does not

    Answer: The reference source for HttpClient seems to indicate that HttpClient does not have a finalizer. It will thus simply be collected.

    Alright, now some actual background info. I often store certain IDisposables as fields, forcing my class to implement IDisposable. The end user should use using. But what if that does not happen?

    Answer: If you forget/don't call IDisposable.Dispose() on objects implementing IDisposable, everything I've stated here regarding finalizers will still happen, once the object is eligible for collection. Other than that, nothing special will happen. Whether the object implements IDisposable or not have no bearing on the garbage collection process, only the presence of a finalizer has.

    Is it merely unnecessary memory until GC? Or do you suddenly have a memory leak

    Answer: Undetermined from this simple information. It depends on what the Dispose method would do. For instance, if the object has registered itself somewhere so that there is a reference to it, somewhere, for some code to stop using the object may not actually make the object eligible for collection. The Dispose method might be responsible for unregistering it, removing the last reference(s) to it. So this depends on the object. Merely the fact that the object implements IDisposable does not create a memory leak. If the last reference to the object is removed, the object becomes eligible for collection and will be collected during a future collection cycle.


    Remarks:

    • Note that the above text is also probably simplified. A full collection cycle to actually "collect memory" is probably not done on application termination as there is no point. The operating system will free the memory allocated by the process when it terminates anyway. Probably only finalization is performed. (meaning, I have no knowledge one way or another what kind of optimizations is done here)

    • The more important part here is that you need to distinguish between memory (or other) leaks during program execution and after program execution

      • When the process terminates, the operating system will reclaim all memory allocated to it, it will close all handles (which may keep sockets, files, etc. open), all threads will be terminated. In short, the program is completely removed from memory
      • The process may have left tidbits of itself around though, which are not cleaned up unless the process took care to do this beforehand. An open file is closed, as stated above, but it may not have been completely written and thus may be corrupt in some way.
      • During program execution, leaks may make the program grow in terms of allocated memory, it may allocate too many handles because it fail to close the ones it no longer needs, etc. and this is important in terms of handling IDisposable and finalizers correctly, but when the process terminates, this is no longer a problem.

    这篇关于C#应用程序退出是否会自动处置托管资源?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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