C# 有东西阻塞了我的内存,程序挂起在GC.Collect()或GC.GetTotalAmountOfMemory上(true)

C# 有东西阻塞了我的内存,程序挂起在GC.Collect()或GC.GetTotalAmountOfMemory上(true),c#,memory-leaks,garbage-collection,block,C#,Memory Leaks,Garbage Collection,Block,无论何时调用GC.Collect()或GC.GetTotalAmountOfMemory(true)整个程序都会挂起。现在,我知道你要说什么了:不要调用GC.Collect()GC会自行处理。问题是:在我的例子中没有,内存中挂着一些东西,GC无法摆脱它 我不知道会是什么(项目很大/很老)。我希望有人知道如何获取有关GC尝试做什么(显然不能做)的信息,以便我可以搜索问题,或者类似内存分析器的东西是否有帮助(我目前安装了ANTS)以及我应该搜索什么 *编辑 我完全忘记了一件事:有时,当我调用GC的两

无论何时调用
GC.Collect()
GC.GetTotalAmountOfMemory(true)
整个程序都会挂起。现在,我知道你要说什么了:不要调用
GC.Collect()
GC会自行处理。问题是:在我的例子中没有,内存中挂着一些东西,GC无法摆脱它

我不知道会是什么(项目很大/很老)。我希望有人知道如何获取有关GC尝试做什么(显然不能做)的信息,以便我可以搜索问题,或者类似内存分析器的东西是否有帮助(我目前安装了ANTS)以及我应该搜索什么

*编辑 我完全忘记了一件事:有时,当我调用GC的两种方法之一时,内存使用量增加了约10千字节/秒

*编辑2 我必须纠正自己:GC.Collect()运行得很好。只有GC.GetTotalMemory(true)挂起。GC.GetTotalMemory(false)也可以正常工作

*编辑3 是的,正如你们中的一些人已经说过的:它似乎是某个地方的终结器。当我运行WaitForPendingFinalizers()时,它只是坐在那里,什么也不做。

好吧,现在我完全糊涂了:当我在Visual Studio的即时窗口中输入
GC.GetTotalMemor(true)
GC.WaitForPendingFinalizers()
时,在我暂停调试器后,程序挂起(我一直都是这样做的……我现在觉得自己很愚蠢)。当我把它添加到我通常的源代码中并编译它时,它工作得很好


很抱歉整件事,看来记忆袋还有别的原因

您是否考虑过使用服务器模式GC设置,以便每个处理器核心运行一个GC线程?
您是否使用性能计数器、转储或探查器调查过该问题?如果应用程序本身不会因为使用ANTS Memory profiler而变得太慢,那么ANTS Memory profiler非常有用。

我还将使用Windows Process Explorer查看您的应用程序,以了解内存端正在发生的情况。

是否覆盖任何终结器?当终结器没有返回时(可能您正在等待另一个线程),应用程序可能会挂起。
GC.Collect()
调用终结器,但它永远不会返回,因为它等待终结器返回。这也可能是内存泄漏的原因,因为GC无法完成垃圾收集

解决方案 您应该能够调试它,当GC正在收集并按下“暂停”键时,它应该会显示它挂起的堆栈跟踪。如果一切都出了问题,您可能需要检查应用程序挂起的反汇编

GetTotalMemory(真) 此方法强制垃圾收集,因此与
GC.Collect()
相同

更新 让我们看看GetTotalMemory:

public static long GetTotalMemory(bool forceFullCollection)
{
    long totalMemory = GC.GetTotalMemory();
    if (!forceFullCollection)
    {
        return totalMemory;
    }
    int remainingSteps = 20;
    long lastMemory = totalMemory;
    float num4;
    do
    {
        GC.WaitForPendingFinalizers();
        GC.Collect();
        totalMemory = lastMemory;
        lastMemory = GC.GetTotalMemory();
        num4 = (float)(lastMemory - totalMemory) / (float)totalMemory;
    }
    while (remainingSteps-- > 0 && (-0.05 >= (double)num4 || (double)num4 >= 0.05));
    
    return lastMemory;
}
正如您所看到的,
GetTotalMemory
正在等待终结器,因此它必须是一个挂起应用程序的终结器
GC.Collect()
在终结器完成时被调用,并且永远不会被调用,因为它以前从未通过该行

在应用程序中的任何一点(不安全代码?另一个线程?)都存在分配大量内存的情况,而GC从来没有机会清理现有内存

免责声明
这纯粹是猜测,我不知道GC是否能处理这种情况,但我认为它无法中断终结器的执行,因为这可能需要很长时间,GC不知道他是否正在挂起或仍在终结。

如果内存泄漏,调用
GC.Collect()
将无助于GC.Collect()或GetTotalAmountOfMemory()只能在堆损坏时挂起。查找错误的pinvoke或COM代码。它是挂起的,还是仅仅需要很长时间?连接一个调试器并查看程序挂起的位置。它可能是终结器中的死锁,或者(更可能是因为您提到了增加内存使用量)终结器中的无限循环。即时窗口中的GetTotalMemory(true)将使应用程序挂起,我认为这是因为调试器暂停了所有线程(和GC)当GetTotalMemory正在另一个线程中等待完成某项任务时,必须有所不同,因为GC.Collect()实际上可以工作,而GetTotalMemory(true)会阻止整个过程。我也这么认为,这就是为什么我之前没有检查它的原因。同样感谢您,我发现ProcessExplorer(我现在使用了几个月)能够显示GPU性能和程序中运行的线程忘记提到另一个困难的方法是生成和分析转储,但读取转储本身就是一门艺术,我有过痛苦的经历,只要应用程序在活动垃圾收集中,它们就不会工作。