title: gc非托管内存泄露
tags:

  • net
    cover: 'https://tuapi.eees.cc/api.php?category=dongman&type=302'
    abbrlink: 43959cba

    date: 2023-09-10 10:26:18

实现 Dispose 方法 - .NET | Microsoft Learn

public void Dispose()
 {
 Dispose(disposing: true);//调用之后就不需要再调用后面
 GC.SuppressFinalize(this);//请求公共语言运行时不要调用指定对象的终结器。
 }

对于应用创建的大多数对象,可以依赖 .NET 垃圾回收器来进行内存管理。 但是,如果创建包含非托管资源的对象,则当你使用完非托管资源后,必须显式释放这些资源。 最常用的非托管资源类型是包装操作系统资源的对象,如文件、窗口、网络连接或数据库连接。 虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但无法了解如何发布并清理这些非托管资源。

如果你的类型使用非托管资源,则应执行以下操作:

  • 实现清理模式。 这要求你提供 IDisposable.Dispose 实现以启用非托管资源的确定性释放。 当不再需要此对象(或其使用的资源)时,类型使用者可调用 Dispose。 Dispose 方法立即释放非托管资源。

  • 在类型使用者忘记调用 Dispose 的情况下,请提供一种方法来释放非托管资源。 有两种方法可以实现此目的:

  • 使用安全句柄包装非托管资源。 这是推荐采用的方法。 安全句柄派生自 System.Runtime.InteropServices.SafeHandle 抽象类,并包含可靠的 Finalize 方法。 在使用安全句柄时,只需实现 IDisposable 接口并在 Dispose 实现中调用安全句柄的 IDisposable.Dispose 方法。 如果未调用安全句柄的 Dispose 方法,则垃圾回收器将自动调用安全句柄的终结器。

    —或—

  • 定义终结器。 当类型使用者无法调用 IDisposable.Dispose 以确定性地释放非托管资源时,终止会启用对非托管资源的非确定性释放。

     警告

    对象终止是一项复杂且易出错的操作,建议你使用安全句柄而不是提供你自己的终结器。

然后,类型使用者可直接调用 IDisposable.Dispose 实现以释放非托管资源使用的内存。 在正确实现 Dispose 方法时,安全句柄的 Finalize 方法或 Object.Finalize 方法的重写会在未调用 Dispose 方法的情况下阻止清理资源。


大概意思是说要显示调用dispose,dispose会通知 终结器线程 去处理:终结器线程会通过线程、进程交流 与对应的线程去释放;

参考com组件的STA单线程套间

终结器(以前称为析构器)用于在垃圾回收器收集类实例时执行任何必要的最终清理操作。 在大多数情况下,通过使用 System.Runtime.InteropServices.SafeHandle 或派生类包装任何非托管句柄,可以免去编写终结器的过程。

类似C++析构函数

在使用时,显示调用Dispose()方法,可以及时的释放资源,同时通过移除Finalize()方法的执行,提高了性能;如果没有显示调用Dispose()方法,垃圾回收器也可以通过析构函数来释放非托管资源,垃圾回收器本身就具有回收托管资源的功能,从而保证资源的正常释放,只不过由垃圾回收器回收会导致非托管资源的未及时释放的浪费

GC垃圾回收,GC.WaitForPendingFinalizers() - 禅道 - 博客园 (cnblogs.com)

由于垃圾回收是异步的,CLR有一个专用的线程负责垃圾回收,因此,即使调用GC.Collect,也并不是实时的调用了Finalize,因此要保证确实调用了析构方法,可以使用语句GC.WaitForPendingFinalizers()。

 GC.Collect();//异步,
 GC.WaitForPendingFinalizers();//确保释放

.NET 垃圾回收 | Microsoft Learn

Dispose由于必须显式调用 方法,因此始终存在不释放非托管资源的危险,


GDI

.NET 程序的 GDI 句柄泄露的再反思-51CTO.COM

GDIView工具不是很准


Using UMDH to Find a User-Mode Memory Leak - Windows drivers | Microsoft Learn