使用IDisposable释放内存和不使用IDisposable释放内存

4
在我的应用程序中,每隔几秒钟就会创建一个大对象。我对它进行了一些操作,然后不再需要它。
我在任务管理器中看到,即使我没有任何对该对象的引用并且需要对其进行回收,RAM大小也会增加。
实现“IDisposable”之后,RAM立即下降。
为什么会这样?我没有执行“GC.Collect”,我只是释放了对象并告诉GC不需要为我的对象调用终结器。
编辑:
以下是我用于“IDisposable”对象的代码:
public void Dispose()
{
  base.Dispose();
  Dispose(true);
  GC.SuppressFinalize();
}

private void Dispose(bool disposing)
{
  //here i release unmanaged resources
  if (disposing)
  {
    //here i release all managed resources
  }
}

~MyObject()
{
  Dispose(false);
}

在我的大对象中,当我不再需要它时,我会执行myObject.Dispose();。我的问题并不是关于如何实现IDisposable或GC的工作原理。我只想知道当我自己处理对象或让GC完成其工作时,有何区别。

GC.Collect非常有问题,会导致性能问题。GC本身很聪明,会在需要时自动启动。 - Zenwalker
1
请展示这个“大”对象的代码和Dispose方法的实现。仅仅在一个对象上拥有IDisposable并不能神奇地释放内存,因为CLR和GC对IDisposable对象没有任何特殊处理。只有Finalizer方法具有特殊意义。 - Steven
“然后RAM立即下降”。您使用什么方法来分析已使用的RAM下降了? - Steven
@zenwalker 我知道这个,我不会使用GC.Collect。 - user1226847
1
这是你的 Dispose(bool disposing) 方法的确切代码,还是你实际上在 //here i release unmanaged resources 行上有代码? - Steven
4个回答

1

在.NET中,内存通常不是只有两种状态(已使用和未使用),而实际上是四种:(A)由活动对象使用,(B)由死亡对象使用,(C)没有任何对象使用,但由框架使用,以及(D)未使用。

当您创建一个对象时,框架将首先尝试为其使用类别(C)的内存。如果可用的内存不足,则会请求类型(D)的操作系统的内存,并将其转换为(C),然后用于您的对象。

当对象超出范围时,它将从(A)降至(B),在下一次垃圾回收运行时,它将从(B)转移到(C)或(D)。这严重取决于内部结构(考虑内存碎片化和友好性),内存压力以及所收集的对象类型(考虑IDisposable和友好性)。

您想要实现的是,在您的大对象超出范围后,尽快使其使用的内存进入(D)。以下是一些提示:

  • 确保您的对象大小是4K的倍数 - 这样很有可能,没有其他对象与您的对象共享内存页,因此更容易将其归还给操作系统

  • 尝试在对象处理周围使用AddMemoryPressure()括号进行实验,但要注意副作用。这类似于强制执行GC.Collect(),但不会过于侵入式:它会提示GC尽快收集,但不是立即收集。

  • 重新思考您的基本概念:大对象是否为单例(即任何时候只能存在一个)?如果是,则考虑分配一次并回收它:这将使您的应用程序RAM需求更可预测,并可能在运行时避免丑陋的OOM情况。


谢谢你的回答!关于第一个问题,如何做到呢?这真的有助于释放内存吗?2.我宁愿不使用它。3.它不是单例的,我真的没有改变整个应用程序设计的选择。 - user1226847
至少有三类对象:根对象、不可达对象和可终结对象(通过终结队列可达,但不通过任何其他根引用可达的对象)。只有完全不可达的对象才能被销毁。如果已注册终结,则可终结对象将从终结队列移动到可终结队列;然后系统会在适当的时候运行可终结队列中所有对象的终结方法。 - supercat
从开发者的角度来看,rootedUsed by live object相同,我的(A),unreachableUsed by dead object,我的(B),freachable和其他尚未可以释放给操作系统的部分是Not used by any object, but by the framework,我的(C)。因此,您的分类与我的非常相似,唯一的区别在于,您的角度是框架如何处理内存,而我的角度是开发者如何处理它。 - Eugen Rieck
@EugenRieck:需要将可回收的对象视为它们自己的类别;系统保证它们的成员将是可访问的(这意味着访问这些成员将导致底层代码执行,而不是这些代码一定会做任何有用的事情),并且这些对象可能会被“复活”。 - supercat

1
正如@Steven在他的评论中指出的那样,IDisposable是CLR不关心的东西。通过实现它,您只需告诉对象的消费者在不再需要对象时调用其Dispose方法即可。在内存管理方面,您可以为此制定自己的接口,例如IDisposable2,并获得相同的技术效果。然而,这样做是愚蠢的,因为.NET开发人员应该知道在不再需要对象时调用IDisposable.Dispose。此外,还有内置的语言(C#)支持此接口(使用{...})。
您写道:“我尝试实现IDisposable,然后RAM立即下降。”
重要的是您的“实现”所做的事情。代码应清理未经管理的代码、Windows资源等。

0
使用IDisposable意味着您正在实现Dispose方法,在其中放置所有不再需要的资源的清理代码。它不会将对象从托管堆中删除,GC仍将负责释放内存。

-2

当你实现IDisposable并且处理对象时,你标记该对象可以进行垃圾回收。

但是你无法准确预测何时进行回收。


4
这不正确。 IDisposable 对于CLR和GC没有任何意义。调用dispose方法不会将对象标记为可供垃圾回收。只有当一个对象不再被引用时,GC才会收集该对象。 - Steven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接