在循环和线程和清除集合中调用GC.Collect不会减少内存使用量

时间:2013-11-25 02:48:22

标签: c# memory garbage-collection

我有很多对象,每个对象都有很多成员,我需要在每个成员中插入一些集合数据并在使用它们后再次清除它们。在我清除之后我希望GC.Collect()可以立即声明内存使用情况,但它的外观并没有减少内存使用量。我检查任务管理器总是在增加。只有在完成所有处理任务后,我才注意到内存使用率已经下降。

在我的电脑上,内存使用量达到10G几乎达到100%..并且在完成所有处理后仅使用率下降...我担心如果客户端pc内存不够则会导致outofmemory异常

我注意到清除所有收集数据看起来不像减少内存使用量。 我应该如何申请记忆?

并且可以在线程内的循环内调用GC.Collect吗?

插图看起来像这样。 例如:

public class Progress 
{
   var obj2 = new Obj2();

   public void Processing()
   {
       //here my thread start.. (I have some thread class)
       AsyncClass.DoTask(() =>
       {
           foreach(var curProcess in AllObjects)
           {

             var allSolutions = (from m in curProcess.memories
                               where.... 
                               select m).ToList();

            forearch(var memory in allSolutions)
            {  
               foreach(data in alldata)
               {
                 //some process
                 ...
                 ...
                 result = data.result;

                 //pushing the data into memory members obj
                  obj2.PushCalculation(memory, result)
               }
             }

            forearch(var memory in allSolutions)
            {  
                obj2.ClearValues(memory)
            }

             GC.Collect();
             GC.WaitForPendingFinalizers();
         }
      });
    }
  }

  public class Obj2 : IDisposable
  {
    public void PushCalculation(memoryObj mem, List<data> results)
    {
       foreach(var result in results)
       {
          mem.data1.add(result);
          mem.data2.addrange(result * 1);
            //etc... all about pushing into memory object members
       }
    }

    public void ClearValues(memoryObj mem)
    {
       // clear all collections of memoryObj members 

       mem.data1.Clear();
       mem.data1 = null;

       mem.data2.Clear();
       mem.data2 = null;

       ...... 
       ......
    }

  }

3 个答案:

答案 0 :(得分:4)

首先:你应该让垃圾收集器(GC)完成它的工作。根据系统,当前负载,应用程序配置文件和其他因素,它可以判断何时收集垃圾的次数比您好得多。

第二:垃圾收集器分配一些内存空间来操作。即使收集器正确收集了大部分空间,这个空间也不会缩小。然后它是空的,但可用于将来的分配;或者它可能会在以后收缩。

但是如果垃圾收集器实际上无法收集您的某些对象,那么您仍然可以在某处对这些对象进行引用。也许在静态字段或你忘记的集合中?

您应该从中获取的是:从不致电GC.Collect()。致电GC.Collect()的理由很少。如果您有一些内存问题促使您使用GC.Collect(),那么您应该调查导致内存问题的原因。

我可以从头脑中想到的可能原因:

  • 保持对您不再需要的对象的引用。
  • 使用非托管资源,然后不正确处理它。

答案 1 :(得分:1)

你的代码可能并不真实,但无论如何我都会尝试回答。

当您致电PushCalculation时,您会向datadata个集合添加mem.data1类对象(假设mem.data2不是结构体)的指针。因此,在该操作之后,您有两个对每个数据对象的引用。一个来自mem.data,另一个来自data.results

然后在ClearValues中清除每个data对象中的一个引用,但results仍将它们全部固定。如果您希望垃圾收集器释放内存,您还需要清除data.results

换句话说,你有内存泄漏。请参阅此答案,了解如何调查内存泄漏。 How to debug the potential memory leak? 我个人使用WinDbg并发现它非常有用。它显示了内存中对象的引脚

答案 2 :(得分:1)

垃圾收集器是复杂的野兽,它们的界面的简单性是骗人的。只需致电GC.Collect(),对吧?

.NET垃圾收集器是一代分析GC。当它运行时,它会查找 new 对象以先摆脱它们。这是因为绝大多数对象都是短暂的。幸存下来的对象被放置在不同的内存池中,并且不会经常扫描此池。如果我没记错的话,有三个这样的池。这意味着当GC执行运行时,它不会扫描整个堆。

另请注意,这些是。这意味着当虚拟机想要分配内存时,它会在尝试扩展堆之前在池中查找可用内存。只有在未立即回收收集的对象的内存时,才可以执行此操作。换句话说,当GC收集对象时,它不一定会将内存返回给操作系统。

这导致我们GC.Collect()。我们知道,当GC运行时,它不会扫描整个堆,当它破坏对象时,它不会返回内存。那么,调用GC.Collect()有什么意义呢?我无法回答那个问题。就我而言,调用GC.Collect()是没用的。