为我尚未设置为null的对象释放内存

时间:2010-10-08 15:15:01

标签: c# .net list memory console-application

编辑:问题与问题无关。我的代码确实有问题,实际上,它很简单,我不想把它放在互联网上。不管怎样,谢谢。

我读了大约550k的Active目录记录并将它们存储在List中,该类是AD用户的简单包装器。然后,我将ADRecords列表分成四个列表,每个列表包含总计的四分之一。在我这样做之后,我将数据库中的大约400k记录(称为EDR记录)读入DataTable。我占据了我的四个季度,并产生了四个线程,通过了四个季度中的每一个。我现在必须使用电子邮件将AD记录与EDR记录相匹配,但我们计划在以后添加更多内容以供匹配。

我在AD记录列表中有一个foreach,在其中,我必须在EDR记录上运行for循环来检查每个记录,因为如果AD记录匹配多个EDR记录,那么就是不是直接匹配,不应该被视为直接匹配。

我的问题,当我在列表中找到这个foreach时,我的ADRecords列表中只有大约130条记录,但是在我将它们全部拉入之后,我Console.WriteLine计数,它是544k。< / p>

我开始认为即使我没有将列表设置为null以便稍后收集,但是C#或Windows或其他东西实际上是我的列表,以便为EDR记录腾出空间,因为我没有使用过暂时列出一下。我必须用来读取EDR记录的数据库是一个链接服务器,因此全部读取它需要大约10分钟,因此我的列表实际上是空闲10分钟,但它永远不会设置为空。

有什么想法吗?

//splitting list and passing in values to threads.
List<ADRecord> adRecords = GetAllADRecords();
        for (int i = 0; i < adRecords.Count/4; i++)
        {
            firstQuarter.Add(adRecords[i]);
        }
        for (int i = adRecords.Count/4; i < adRecords.Count/2; i++)
        {
            secondQuarter.Add(adRecords[i]);
        }
        for (int i = adRecords.Count/2; i < (adRecords.Count/4)*3; i++)
        {
            thirdQuarter.Add(adRecords[i]);
        }
        for (int i = (adRecords.Count/4)*3; i < adRecords.Count; i++)
        {
            fourthQuarter.Add(adRecords[i]);
        }
        DataTable edrRecordsTable = GetAllEDRRecords();

        DataRow[] edrRecords = edrRecordsTable.Select("Email_Address is not null and Email_Address <> ''", "Email_Address");
        Dictionary<string, int> letterPlaces = FindLetterPlaces(edrRecords);
        Thread one = new Thread(delegate() { ProcessMatches(firstQuarter, edrRecords, letterPlaces); });
        Thread two = new Thread(delegate() { ProcessMatches(secondQuarter, edrRecords,  letterPlaces); });
        Thread three = new Thread(delegate() { ProcessMatches(thirdQuarter, edrRecords,  letterPlaces); });
        Thread four = new Thread(delegate() { ProcessMatches(fourthQuarter, edrRecords, letterPlaces); });
        one.Start();
        two.Start();
        three.Start();
        four.Start();

在ProcessMatches中,传入的ADRecords列表中有一个foreach。foreach中的第一行是AdRecordsProcessed ++;这是一个全局静态int,程序在130而不是544k完成。

4 个答案:

答案 0 :(得分:4)

变量永远不会设置为null并且仍在范围内?如果是这样,就不应该收集它,空闲时间不是你的问题。

我看到的第一个问题是:

AdRecordsProcessed++; 

您在更新之前是否锁定了该全局变量?如果没有,并且取决于记录处理的速度,它将低于您的预期。

尝试从单个线程运行它(即传入adRecords而不是firstQuarter而不启动其他线程。)是否按预期使用1个线程?

答案 1 :(得分:2)

首先,您不要将列表设置为null。您可能要做的是将列表的每个引用设置为null(或另一个列表),或者所有此类引用可能只是超出范围。这可能看起来像是一个挑剔点,但如果你不得不检查你的数据发生了什么,那么就是时候对这些事情挑剔了。

其次,让GC取消分配具有实时引用的内容非常困难。您可以使用WeakReference&lt;&gt;伪造它或者当你在终结者中遇到一个错误时认为你已经找到了它(因为引用实际上并不存在,即使这样,它也是终结者试图处理最终化而不是解除分配对象的问题)。错误可能发生在任何地方,但是你已经找到了一种方法来让GC解除分配现状,这是非常不可能的。

GC可能会对您的列表做两件事:

  1. 很可能会压缩它所使用的内存,这会移动其组件项目。
  2. 很有可能将它推广到更高的一代。
  3. 除非你真正找到它们,否则这些都不会有任何你会发现的变化(显然你会注意到如果你继续调用GetGeneration()会产生一代变化,但除此之外你不会真正去)。

    使用的内存也可以被分页,但是当你去使用这些对象时它会被重新分页。再一次,你不会注意到任何影响。

    最后,如果GC确实释放了某些东西,你就不会有减少的项目数量,你就会崩溃,因为如果对象刚被解除分配,系统仍会尝试使用所谓的实时引用。

    因此,虽然GC或操作系统可能会为您的其他对象腾出空间,但它不是代码中可观察到的东西,并且它不会阻止对象可用并处于相同的编程状态。

    还有其他问题。

答案 2 :(得分:1)

您是否有必要同时获取所有数据?如果将数据分成块,它应该更易于管理。我所知道的只是进入GC的东西有点臭。最好看看重构代码。

答案 3 :(得分:1)

垃圾收集器收集:

  • 全局变量
  • 由静态对象管理的对象
  • 本地变量
  • 可通过调用堆栈上的任何方法引用的变量

因此,如果您可以从代码中引用它,则垃圾收集器不可能收集它。没办法,没办法。

为了让收集者收集它,所有对它的引用都必须消失。如果你能看到它,那绝对不是这样的。