在终结器中调用GC.Collect是否合适? [英] Is it appropriate to call GC.Collect in a finalizer?

查看:193
本文介绍了在终结器中调用GC.Collect是否合适?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个BitmapImage类,该类包装了本地图像ID和System.Drawing.Bitmap并实现了IDisposable.图像ID是作为我们使用的第三方成像SDK的一部分创建的,该SDK使用整数来标识指向某些像素数据的图像".图像ID和位图都指向相同的基础像素数据.

We have a BitmapImageclass that wraps a native image id and System.Drawing.Bitmap and implements IDisposable. The image id is created as part of a third-party imaging SDK we use that uses integers to identity an "image" pointing to some pixel data. Both the image id and bitmap point to the same underlying pixel data.

在很多情况下,SDK很难正确使用 ,因此我们有一个Facade层,可以将SDK提取出来,并为TIF和PDF文档提供易于使用且无错误的API. ,这部分是为了确保尽快释放内存.常见的300 DPI多页面图像具有数百个页面,因此在应用程序中可以轻松地占用大量内存.

The SDK is very difficult to use correctly in many cases, so we have a facade layer that abstracts away the SDK and provides an easy to use and error-less API, for TIF and PDF documents, and part of this is to guarantee that memory is freed as soon as possible. 300 DPI multi-page images with hundreds of pages are common place, so memory can easily be high in the application.

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

We are currently calling GC.Collect in the Dispose method to free up the memory immediately. After thorough testing of the software, this was the only way to free up large amounts of memory immediately after releasing the underlying pixel data, especially during a large merge operation where we might be merging several hundreds of pages together in a document. It is also implemented this way so developers do not incorrectly try to scatter their code with GC.Collect, because they should not really be calling it anyway.

我的问题分为两个部分:

My question is two parts:

  • 当垃圾回收器调用终结器时,它是否还会立即释放内存,还是可能需要很长时间才能发生?我们也应该在这里调用GC.Collect吗?尤其是在只有32位的过程中,我们必须确保保持尽可能多的可用内存.
  • 我们使用的SDK是GdPicture,即使您Dispose使用其图形对象,它也不会处理像素数据或图像引用.它留给他们,直到开发人员释放它们.我们需要保证,如果开发人员不手动调用Dispose,则资源将被释放.在终结器(例如GraphicsObject.ReleaseImage(id))中引用托管类是否合适?我在某些地方读到,除了SafeHandle之类的静态方法外,您不应该调用其他方法,但是除非我们调用此方法,否则不会释放内存
  • When a finalizer is called by the garbage collector, does it also free the memory immediately, or could there be a long period of time before this occurs? Should we call GC.Collect here as well? Especially in only a 32-bit process, we must ensure we are keeping as much free memory as possible.
  • The SDK we use is GdPicture, and even when you Dispose of their graphics object, it does not dispose of the pixel data or image references. It leaves them around until a developer frees them. We need to guaruntee that if a developer does not call Dispose manually, that the resources are freed. Is it appropriate to reference a managed class in a finalizer, such as GraphicsObject.ReleaseImage(id)? I read in some places that you should not call methods other than some of the static ones from things like SafeHandle, but unless we call this the memory will not be freed

推荐答案

我认为这里有些混乱.希望在常识中更加清楚.

I think there's a bit mess. Want to make it more clear in common understanding.

您无法管理GCollection的时间.

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

GC-n occurs when: System has low physical memory + Memory that is used by allocated objects on the managed heap surpasses an acceptable threshold. This threshold is continuously adjusted as the process runs. + GC.Collect method is called. In almost all cases, you don’t have to call this method, because GC runs continuously. GC.Collect method is primarily used for unique situations and testing.

GC优化引擎根据所做的分配确定执行收集的最佳时间. 终结器执行的确切时间是不确定的.为确保确定释放类实例的资源,请实现Close()方法或提供IDisposable.Dispose实现+不能保证两个对象的终结器以任何特定顺序运行+未指定运行终结器的线程.如果有人忘记处理,请使用Destructor

GC optimizing engine determines best time to perform a collection, based upon allocations being made. Exact time when Finalizer executes is undefined. To ensure deterministic release of resources for instances of class, implement a Close() method or provide an IDisposable.Dispose implementation + The finalizers of two objects are not guaranteed to run in any specific order + Thread on which the finalizer runs is unspecified. For case someone forget to Dispose use Destructor

〜YourClass(){Dispose(false);}

~ YourClass(){Dispose(false);}

被转换为:

受保护的覆盖无效Finalize(){尝试{…清理…}最后{base.Finalize(); }

protected override void Finalize() { try { … cleanup…} finally { base.Finalize(); } }

  • 别忘了
  • 您的班级:IDisposable

    YourClass : IDisposable

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

    It's ("Finalizer") used to perform cleanup operations on unmanaged resources held by current object before object is destroyed by GC. Method is protected and therefore is accessible only through this class or through a derived class. By default (when use ~), GC automatically calls an object's finalizer before reclaiming its memory.

    GC将类型的每个实例的条目添加到内部结构完成队列(由GC控制的内部数据结构(队列))中.终结队列包含托管堆中所有对象的条目,这些对象的终结代码必须运行,GC才能回收它们的内存.当GC发现对象是垃圾时,它将扫描Finalization Queue,以查找指向这些对象的指针.如果找到了指针,则将其从Finalization Queue中删除,并将其添加到Freachable Queue中.对象不再被视为垃圾,并且不回收其内存.至此,GC已完成识别垃圾.特殊的运行时线程清空Freachable Queue,执行每个对象的Finalize方法.通常,当Freachable Queue为空时,此线程将进入睡眠状态.

    GC adds an entry for each instance of the type to an internal structure finalization queue - an internal data structure (Queue) controlled by GC. Finalization queue contains entries for all objects in managed heap whose finalization code must run before GC can reclaim their memory. When GC finds objects to be garbage, it scans Finalization Queue looking for pointers to these objects. If a pointer is found, it is removed from Finalization Queue and added to Freachable Queue. Object is no longer considered garbage and its memory is not reclaimed. At this point, GC has finished identifying garbage. Special runtime thread empties the Freachable Queue, executing each object's Finalize method. Normally when Freachable Queue is empty, this thread sleeps.

    对于具有析构函数的对象,您至少需要2个GCollections(这就是在Dispose模式下使用GC.SuppressFinilise(this)的原因-您对GC说,不要将对象移至Generation_1或Generation_2,当满足GC要求时,可能会回收内存) + (这也意味着带有终结器的对象最少移至Generation_1,GC很少检查10次(比Generaation_0少),这意味着不需要的对象在内存中的停留时间更长)

    You need minimum 2 GCollections for objects with destructor (That's why in Dispose pattern GC.SuppressFinilise(this) is used - you say to GC that don't move object to Generation_1 or Generation_2, that memory might be reclaimed as GC meet destructor) + (that also means that objects with finalizers are moved minimum to Generation_1, that GC checks about 10 times rarely (than Generaation_0) that means that objects you don't need stay in memory longer)

    建议申请或

    正在使用(...)

    using(...)

    转化为

    最后尝试{…} {XX.Dispose();}

    try { … } finally { XX.Dispose();}

    或使用IntPtr结构对非托管资源正确实现 IDisposable 接口(它适用于不同的体系结构(32或64)). 请勿建议使用SafeHandle(Interopservices),因为它仅适用于Win32体系结构(SafeHandle的优点是您不需要实现IDisposable,因为它是从CriticalFinalizerObject继承的)

    or implement correctly IDisposable interface using IntPtr structure for unmanaged resources (it works with different architecture (32 or 64)). Do not recommend to use SafeHandle (Interopservices) because it works only with Win32 architecture (good in SafeHandle is that you don't need to implement IDisposable as it inherits from CriticalFinalizerObject)

    IDisposable的经典实现,当您不需要关心是否有人忘记处理对象时(对于这种情况,您有〜析构函数):

    对于基类

    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)}   }
    

    处理错误–调用来自Finalizer;处置真实–从处置(来自您)

    disposing false – call comes from Finalizer; disposing true – from Dispose (from you)

    对不起,忘了继续:

    ** 使用GC.Collect强制系统尝试"以回收最大可用内存量.您也可以指出GC您要检查哪几代.但是,正如我在开始时所写的那样-即使使用GC.Collect,您也无法控制-这不能保证任何事情(GC可能会覆盖您的呼叫等),但是您可以尝试并且不会带来任何问题 **

    这篇关于在终结器中调用GC.Collect是否合适?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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