【问题标题】:Dispose() does not prevent out of memory exceptionDispose() 不能防止内存不足异常
【发布时间】:2012-03-31 04:37:17
【问题描述】:

我有一个巨大的日志文件,其中大约有 1500 万行。我必须将所有这些行移植到 Oracle 数据库。这是我要做的:

  1. 我读取数据表的行(行用逗号分隔,这样 它们形成列)。我通过 StreamReader 阅读了这些行并将其放入 使用,以便它在完成阅读后摆脱它。
  2. 我放置了一个计数器,该计数器每读取到 DataTable 的行就加 1。
  3. 当该计数器达到 40.000 时,我在 OracleBulkCopy 的帮助下将所有行导入数据库。
  4. 导入完成后我会说:

    bulkCopier.Dispose();
    dataTable.Dispose();

但是,这无济于事。在任务管理器中,我看到内存使用量不断增长,直到内存不足。现在我的问题是Dispose() 方法不会立即释放内存吗?如果是这样,那么为什么内存使用量会累积,好像所有 DataTable 和 OracleBulkCopy 对象都在保留内存一样。

【问题讨论】:

  • 我认为您假设流是问题所在,而且可能是错误的。这些字符串有多大?如果它们是在 LOH 上分配的,那么您可能会导致大量碎片。使用分析器查看内存分配的位置。
  • GC.Collect() 在这些情况下可能会有所帮助,或者为 64 位编译应用程序。尽管即使在 32 位上,如果内存压力过高,GC 也会自动运行。也许对象太大了,它们会放在大对象堆上?
  • 获取内存分析器并使用它来确定内存中的内容。我们无法知道您的机器上发生了什么。
  • 我遵循@Eric Lippert 和 Ed S. 的建议并使用分析器进行了检查,它确实是导致问题的流阅读器。现在我需要找到一种方法,从我停止使用新的流读取器对象的地方开始。谢谢大家。
  • 如果您遇到内存问题,请转储 DataTable - 它会占用内存。到目前为止,.NET 中的集合已经出现。我不了解 Oracle,但在 MSSQL 中,我使用 TVP(表值参数)获得了最快的上传,我将它们与集合(HashTable)而不是 DataTable 一起使用。它就像一个引用 DataReader。我解析一些文本文件并加载单词并每晚加载数百万。我在主线程上解析,然后每 10,000 行使用 BackGround 工作线程加载到另一个线程上的 SQL。

标签: c# dispose out-of-memory


【解决方案1】:

Dispose 不会立即释放托管内存。 Dispose 在调用 dispose 时会释放对非托管资源(如内存)的所有锁,与托管内存无关。

您的数据表全部填充了托管内存中的托管对象。调用 .Dispose 不会做任何事情。您需要从 DataTable 中清除所有数据(有一个方便的 Clear 方法可以做到这一点),这将允许垃圾收集器在未来某个时间点释放内存,当它感觉需要清理时。

使用分析器,您可以查看程序中所有内存的确切保存位置。很可能还有许多其他托管对象您持有对它们的引用,这会阻止它们被垃圾收集。这与Dispose无关。

【讨论】:

    【解决方案2】:

    不,dispose 指示要拆除的内部句柄。具有 Dispose 的对象实际上会在稍后获得 GC-d。

    如果这些是 IDisposable,您应该尝试在 using() 块中使用它们,根据使用后自动释放的文档。

    你可以用 GC.Collect 强制 GC 收集,但我不认为那些对象是未释放的抓取,.NET 比那个更好。

    你不能创建一个小型转储并检查什么被卡住了,为什么?

    【讨论】:

      【解决方案3】:

      现在我的问题是 Dispose() 方法不会释放内存 马上?

      不一定。它将释放的唯一内存是非托管资源分配的内存量。它不会影响托管堆。

      您需要做的是确保没有对这些悬空DataTable 实例的引用。您可能需要根据需要将它们从集合中删除或null 出变量引用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-05
        • 1970-01-01
        相关资源
        最近更新 更多