【问题标题】:C# Too Much Memory UsageC# 内存使用过多
【发布时间】:2018-08-23 13:41:36
【问题描述】:

我有一个流程需要多个步骤。 (假设是策略模式的通用实现)所有步骤都在传递一个通用的 ProcessParameter 对象。 (读/写)

此 ProcessParameter 是一个具有许多数组和集合的对象。示例:

class ProcessParameter() {
   public List<int> NumbersAllStepsNeed {get; set;}
   public List<int> OhterNumbersAllStepsNeed {get; set;}
   public List<double> SomeOtherData {get; set;}
   public List<string> NeedThisToo {get; set;}
   ...
}

一旦这些步骤完成,我想确保内存被释放并且不会闲置,因为这可能会占用大量内存并且其他进程也需要运行。

我是否通过运行来做到这一点:

pParams.NumbersAllStepsNeed = null;
pParams.OhterNumbersAllStepsNeed = null;
pParams.SomeOtherData = null;
...

或者ProcessParameter应该实现IDosposable,Dispose方法会这样做,然后我只需要使用pParams.Dispose()(或将其包装在使用块中)

清理一个正在运行的进程的已用数据的内存占用的最佳和最优雅的方法是什么?

使用数组而不是列表会改变什么吗?还是混合的? 我需要的实际参数类型是自定义对象的集合/数组。

我的方向是否正确?

更新

好问题!感谢cmets! 我曾经让这个进程单独运行,我可以看到内存使用率非常高,然后逐渐下降到“正常”。

当我开始使用不同的声明参数将这些进程链接在一起时,问题就出现了。那是内存异常高的时候,所以我想在两个进程之间包含一个清理步骤,并寻找最好的方法来做到这一点。

有一个数据库,这个参数是一种“缓存”来加快速度。

关于 IDisposable 的要点是,我不会在 params 对象中保留非托管资源。

【问题讨论】:

  • 您是在尝试解决实际问题还是在询问如何实施过早优化?
  • IDisposable 用于释放非托管资源。要使托管对象符合 GC 条件,您只需要确保没有对它的引用。另见What is the correct way to free memory in C#
  • 一旦ProcessParameter 不再可访问并被收集,其属性中包含的所有对象也将被收集。一般来说,除非您使用ProcessParameter 实例的时间比子对象长得多,否则不需要显式地null 它们。如果有这样的需求,你应该在诉诸半手动内存管理恶作剧之前认真考虑拆分对象。对于初学者来说,这样的事情是NullReferenceException 等待发生的事故。
  • 我同意杰罗恩的观点。 “使用”不会有任何帮助,我想从您给出的使用描述推断,生命周期不会在一个“使用”块内处理。我想补充一点,你可能应该小心不要“泄露”引用,所以那些大的引用可以被 GC 回收,仅此而已。你应该没事。
  • 如果不涉及非托管资源,可能 GC.Collect 和 GC.WaitForPendingFinalizers 是您要查找的,而不是 Dispose。然而,在大多数情况下,这样的 hack 是没有用的......

标签: c# memory-management idisposable


【解决方案1】:

虽然使用 Disposal 模式是个好主意,但我认为它不会在释放内存方面给您带来任何额外的好处。

两件事可能:

  1. 调用 GC.Collect()

但是,我真的不会打扰(除非您可能遇到内存不足异常)。显式调用 GC.Collect() 可能会损害性能,而垃圾收集器本身确实做得很好。 (但请参阅下面的 LOH。)

  1. 注意大对象堆 (LOH)

您提到它使用“大内存占用”。请注意,任何 85,000 字节或以上的内存分配都来自大对象堆 (LOH)。 LOH 不会像小对象堆那样被压缩。这可能会导致 LOH 变得碎片化,并且即使您有足够的可用内存也可能导致内存不足错误。

您什么时候可能会误入 LOH?任何 85,000 字节或更多的内存分配,因此在 64 位系统上,可以是具有 10,625 个或更多元素的任何数组(或列表或字典)、图像处理、大字符串等。

三种有助于减少 LOH 碎片化的策略:

我。重新设计以避免它。并不总是实用的。但是列表或字典的列表可能会避免限制。这会使实现更加复杂,所以除非你真的需要,否则我不会这样做,但从好的方面来说,这可能非常有效。

二。使用固定尺寸。如果 LOH 中所有更多的内存分配大小相同,那么这将有助于最大限度地减少任何碎片。例如,对于字典和列表,将容量(设置内部数组的大小)设置为您可能使用的最大大小。如果您在进行图像处理,则不太实用。

三。强制垃圾收集器压缩 LOH:

System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;   
GC.Collect();

您确实需要使用 .NET Framework 4.5.1 或更高版本才能使用它。 这可能是最简单的方法。在我自己的应用程序中,我有几个实例,我知道我会误入 LOH 并且碎片可能是一个问题,我设置了

System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;

作为析构函数中的标准 - 但只有在分配时出现内存不足异常时才显式调用 GC.Collect()。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-04
    • 2012-09-12
    • 2018-04-01
    相关资源
    最近更新 更多