C# 在终结器中调用GC.Collect是否合适?

C# 在终结器中调用GC.Collect是否合适?,c#,garbage-collection,C#,Garbage Collection,我们有一个BitmapImage类,它包装了一个本机图像id和System.Drawing.Bitmap,并实现了IDisposable。图像id是作为我们使用的第三方imaging SDK的一部分创建的,该SDK使用整数标识指向某些像素数据的“图像”。图像id和位图都指向相同的底层像素数据 SDK在许多情况下很难正确使用,因此我们有一个facade层,它抽象了SDK,并为TIF和PDF文档提供了一个易于使用且无错误的API,其中一部分是为了确保尽快释放内存。具有数百页的300 DPI多页图像是

我们有一个
BitmapImage
类,它包装了一个本机图像id和
System.Drawing.Bitmap
,并实现了
IDisposable
。图像id是作为我们使用的第三方imaging SDK的一部分创建的,该SDK使用整数标识指向某些像素数据的“图像”。图像id和位图都指向相同的底层像素数据

SDK在许多情况下很难正确使用,因此我们有一个facade层,它抽象了SDK,并为TIF和PDF文档提供了一个易于使用且无错误的API,其中一部分是为了确保尽快释放内存。具有数百页的300 DPI多页图像是常见的位置,因此应用程序中的内存可能很高

我们当前正在调用
Dispose
方法中的
GC.Collect
,以立即释放内存。在对软件进行全面测试后,这是在释放底层像素数据后立即释放大量内存的唯一方法,尤其是在大型合并操作期间,我们可能会将数百页合并到一个文档中。它也是以这种方式实现的,这样开发人员就不会错误地尝试使用GC.Collect分散他们的代码,因为他们无论如何都不应该真正调用它

我的问题分为两部分:

  • 当垃圾收集器调用终结器时,它是否也会立即释放内存,或者在这种情况发生之前会有很长一段时间?我们是否也应该在这里调用
    GC.Collect
    ?特别是在仅32位进程中,我们必须确保尽可能多地保留可用内存
  • 我们使用的SDK是GdPicture,即使当您
    处理他们的图形对象时,它也不会处理像素数据或图像引用。在开发人员释放它们之前,它会一直保留它们。我们需要保证,如果开发人员不手动调用
    Dispose
    ,资源就会被释放。是否适合在终结器中引用托管类,例如
    GraphicsObject.ReleaseImage(id)
    ?我在一些地方读到,除了从
    SafeHandle
    之类的东西调用一些静态方法之外,不应该调用其他方法,但是除非我们调用它,否则内存将不会被释放

一般来说,生产代码中不应包含GC.Collect。它可以帮助调试(如果您想查看内存峰值是否是临时的)。但一般来说,最好将GC留给它自己的设备。它已经尽可能地优化了。即使它没有收集,它也在做一些有效的事情(如果应用程序关闭可能会使它的工作简单得多的话,那么等待)。如果您想影响主要行为,请更改应用程序的GC策略

你问题的第二部分是关于。有两种情况:

  • 您只处理实现IDisposable的东西。在这种情况下,您需要做的就是实现Dispose。它所需要做的就是将“Dispose”命令传递给它包含的任何可处置的对象
  • 实际上,您可以处理未老化的资源。在这种情况下,您应该首先实现终结器(因此,至少GC可以在应用程序关闭/收集时清理此终结器)。然后提供Dispose函数作为附加功能。但是我总是把终结器放在第一位,因为它会被保证调用
终结器和Dispose在很大程度上是一致的。因此,通常只使用布尔开关编写Dispose函数。Winch Finalizer和Dispose中唯一不同的是“中继”部分: -如果适用,始终将Dispose调用中继到包含的实例。因为这是Dispose的常见用例(中继Dispose()的调用)。如果我们都坚持,那就更好了。 -您从未将fianlizer调用中继到包含的isntances。终结在该实例和GC之间

大约95%的情况下,你只需要处理

在终结器中调用GC.Collect没有任何意义。终结器是关于清理非托管资源的。GC不管理的那些


GC Collect只是“立即收集托管资源”。它们几乎适用于相反的情况(托管和未更改的资源)。

我认为有点混乱。希望在共识中更加明确

您无法管理GCollection的时间。

GC-n发生在以下情况:系统的物理内存不足+托管堆上分配的对象使用的内存超过可接受的阈值。该阈值在进程运行时不断调整调用了GC.Collect方法。几乎在所有情况下,您都不必调用此方法,因为GC是连续运行的。Collect方法主要用于特殊情况和测试

GC优化引擎根据正在进行的分配确定执行收集的最佳时间。 终结器执行的确切时间未定义。要确保类实例的资源得到确定性释放,请实现Close()方法或提供IDisposable。Dispose实现+不保证两个对象的终结器以任何特定顺序运行+未指定终结器运行的线程。如果有人忘记处理,请使用析构函数

~YourClass(){Dispose(false);}

转化为:

受保护的重写void Finalize(){try{…cleanup…}最终{base.Finalize();}

  • 别忘了
你的班级:我可以

它(“终结器”)用于在GC销毁对象之前对当前对象持有的非托管资源执行清理操作。方法受保护,因此只能通过此类或派生类访问。默认情况下(使用~),GC会在回收对象内存之前自动调用对象的终结器

GC为该类型的每个实例添加一个条目
public MyClass() { this.reader = new TextReader(); }

public class A : IDisposable   
{
    bool disposed = false;  // Flag: Has Dispose already been called?
    private Component handle;
    private IntPtr umResource;
    public void Dispose()   {      
                Dispose(true);   GC.SuppressFinalize(this);   }
    protected virtual void Dispose(bool disposing) {
                if (disposed) return;
                if (disposing) {            // Free managed resources
                        if (reader != null) reader.Dispose(); 
                        if (handle != null) handle.Close(); }
                if (umResource != IntPtr.Zero) { // Free unmanaged resources
                            umResource = IntPtr.Zero; }
                disposed = true;   }  
    ~A() { Dispose(false); }   } // Only if we have real unmanaged resources
public class B : A  {
        private bool disposed = false;
        protected override void Dispose(bool disposing)  {
                    if (disposed) return;
                    if (disposing) {  ... }     // To free any managed objects 
                                               // Free unmanaged resources
                    disposed = true;     
                    base.Dispose(disposing)}   }