我们有一个BitmapImage
类,用于包装本机图像ID和System.Drawing.Bitmap
并实现IDisposable
。图像ID是作为我们使用的第三方成像SDK的一部分创建的,它使用整数来标识指向某些像素数据的“图像”。图像ID和位图都指向相同的基础像素数据。
在许多情况下,SDK很难正确使用 ,所以我们有一个外观层来抽象SDK并为TIF和PDF文档提供易于使用且无错误的API ,部分原因是为了保证尽快释放内存。有数百页的300 DPI多页图像很常见,因此应用程序中的内存容易很高。
我们目前正在GC.Collect
方法中调用Dispose
来立即释放内存。在对软件进行全面测试之后,这是在释放基础像素数据后立即释放大量内存的唯一方法,特别是在我们可能在文档中合并数百页的大型合并操作期间。它也是以这种方式实现的,因此开发人员不会错误地尝试使用GC.Collect来分散他们的代码,因为他们不应该真正地调用它。
我的问题分为两部分:
GC.Collect
吗?特别是在32位进程中,我们必须确保尽可能多地保留可用内存。Dispose
,它也不会处理像素数据或图像引用。它让它们四处走动,直到开发人员释放它们为止。我们需要保证,如果开发人员没有手动调用Dispose
,那么资源就会被释放。是否适合在终结器中引用托管类,例如GraphicsObject.ReleaseImage(id)
?我在某些地方读到你不应该调用除SafeHandle
之类的静态方法之外的其他方法,但除非我们称之为内存,否则不会释放内存答案 0 :(得分:0)
我觉得有点乱。希望在共同理解中更清楚。
您无法管理GCollection的时间。
GC-n在以下情况下发生:系统物理内存不足+托管堆上已分配对象使用的内存超过可接受的阈值。在该过程运行时不断调整该阈值。调用+ GC.Collect方法。几乎在所有情况下,您都不必调用此方法,因为GC会持续运行。 GC.Collect方法主要用于独特的情况和测试。
GC优化引擎根据正在进行的分配确定执行收集的最佳时间。 Finalizer执行的确切时间未定义。要确保为类实例确定性地释放资源,请实现Close()方法或提供IDisposable.Dispose实现+两个对象的终结器不保证以任何特定顺序运行+未指定终结器运行的线程。如果有人忘记Dispose使用析构函数
〜YourClass(){Dispose(false);}
转变为:
protected override void Finalize(){try {... cleanup ...} finally {base.Finalize(); }}
YourClass:IDisposable
它是(“Finalizer”)用于在GC销毁对象之前对当前对象持有的非托管资源执行清理操作。方法受到保护,因此只能通过此类或派生类访问。默认情况下(使用〜时),GC会在回收内存之前自动调用对象的终结器。
GC为每个类型实例添加一个条目到内部结构终结队列 - 由GC控制的内部数据结构(队列)。终结队列包含托管堆中所有对象的条目,其最终化代码必须在GC可以回收其内存之前运行。当GC发现对象是垃圾时,它会扫描Finalization Queue,寻找指向这些对象的指针。如果找到指针,它将从Finalization Queue中删除并添加到Freachable Queue。对象不再被视为垃圾,其内存不会被回收。此时,GC已完成识别垃圾。特殊运行时线程清空Freachable Queue,执行每个对象的Finalize方法。通常,当Freachable Queue为空时,该线程会休眠。
对于具有析构函数的对象,您至少需要2个GCollection(这就是为什么在Dispose模式中使用GC.SuppressFinilise(this) - 你说GC不会将对象移动到Generation_1或Generation_2,该内存可能被回收为GC会合析构函数) + (这也意味着具有终结器的对象最小移动到Generation_1,GC很少检查10次(比Generaation_0),这意味着你不需要的对象在内存中停留的时间更长)< / p>
建议申请或
使用(...)
转换为
最后尝试{...} {XX.Dispose();}
或使用IntPtr结构为非托管资源正确实现 IDisposable 接口(它适用于不同的架构(32或64))。 不要建议使用SafeHandle(Interopservices),因为它只适用于Win32架构(SafeHandle的好处是你不需要实现IDisposable,因为它继承自CriticalFinalizerObject)
IDisposable的经典实现,当你不需要关心是否有人忘记Dispose对象时(对于那种情况你有~detructor):
适用于基类
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)} }
处理false - 来自Finalizer的调用;处置true - 来自Dispose(来自你)
抱歉,忘了恢复:
** 使用GC.Collect强制系统尝试回收最大可用内存量。您还可以向GC指出要检查哪些代。但是我正在开始 - 你无法控制,即使你使用GC.Collect - 这不能保证任何事情(GC可能会覆盖你的电话等),但是你可以尝试它不会带来任何问题< / strong> **
答案 1 :(得分:-1)
作为一般规则,您不应该在生产代码中使用GC.Collect。它可以帮助调试(如果你想查看内存峰值是否是临时的)。但通常最好将GC留给它自己的设备。它尽可能地优化。即使它没有收集,它也在做一些有效的事情(等待应用程序关闭可能会使它的工作变得更加简单)。如果您想影响主要行为,请改为为应用程序更改GC策略。
问题的第二部分是Dipose/Finalize pattern。有两种情况:
终结者和处置在很大程度上是不同的。如此相同通常只需用布尔开关编写Dispose函数即可。 Finalizer和Dispose中唯一不同的是&#34; relay&#34;部分: - 如果适用,您始终将Dispose调用中继到包含的实例。因为这是Dispose的常用用例(中继调用Dispose())。如果我们都遵守它,那就更好了。 - 你永远不会将fianlizer调用转发给包含的isntances。最终确定是在该实例和GC之间进行的。
大约95%的案件只需要处理。
在Finalizer中调用GC.Collect毫无意义。终结者是关于清理非托管资源。 GC不管理的那些。
GC Collect现在只是&#34;现在收集托管资源&#34;。它们几乎适用于相反的情况(托管与未管理的资源)。