3

我有一个 .net 2.0 应用程序,它执行一些繁重的处理,使用大量内存,并且做得更多。它从 Web 服务中获取任务,完成它的工作,返回结果,然后无限重复所有这些。

主要代码结构可以通过这个简化和说明

while (true) 
{
     Task t=ServiceAccess.GetTask();
     if (t.TaskType == 1)
     {
          BrutalMemoryConsumerProcessor b=new BrutalMemoryConsumerProcessor();
          b.DoTask(t);
     } 
     else if (t.TaskType == 2)
     {
          HeavyCPUConsumerProcessor h=new HeavyCPUConsumerProcessor();
          h.DoTask(t);
     }
}

对我来说幸运的是,它在几个循环后死于OutOfMemoryException内部代码的某个地方。这是因为这两个对象都已校准为使用几乎所有的 RAM(对于 x86 应用程序),并且在创建新对象实例的同时拥有旧对象实例是进程扼杀的可靠方法。

好的,所以我先尝试了一些技巧。即:

GC.Collect();

在循环的开始,就在while (true).

没运气。

接下来,我想起了旧的基于 VB6 COM 的日子,并b = null;在语句块h = null;的范围内进行了尝试。if

又没有运气了。

结合,没有骰子。

启动了内存分析器。我有一段时间没有使用它,所以我需要浏览几页“程序文件”才能找到它。它是YourKit Profiler。不错的玩具...

无论如何,在对其进行了一些干预之后,它告诉我对本地“堆栈范围”的引用bh停留在本地“堆栈范围”内。所以我做了猴子会做的事情,并在上面重写为:

while (true) 
{
     Task t=ServiceAccess.GetTask();
     if (t.TaskType == 1)
     {
          DoTaskType1(t);
     } 
     else if (t.TaskType == 2)
     {
          DoTaskType2(t);
          HeavyCPUConsumerProcessor h=new HeavyCPUConsumerProcessor();
          h.DoTask(t);
     }
}

private void DoTask1(Task T)
{
    BrutalMemoryConsumerProcessor b=new BrutalMemoryConsumerProcessor();
    b.DoTask(T);
}

private void DoTask2(Task T)
{
    HeavyCPUConsumerProcessor b=new HeavyCPUConsumerProcessor();
    b.DoTask(T);
}

哦,我的。它解决了内存泄漏问题。

因此,通过将对象创建移动到一个肯定会超出范围的函数中,尽管if代码块本身就是范围,但对象被释放和删除。

为什么?

4

1 回答 1

5

您可能未优化运行(在调试模式下,或附加了调试器)。在这种情况下,JIT 将本地变量的生命周期延长到方法的末尾以帮助调试。

使用单独的方法,局部变量只存在于一个单独的堆栈框架中,并且很快就会消失。

在没有调试器的情况下在发布模式下尝试。

于 2013-09-03T15:15:18.890 回答