【发布时间】:2012-10-16 13:56:31
【问题描述】:
我有一段代码使用静态方法Process.GetProcessesByName(String, String) 获取远程计算机上的进程列表,它在很多计算机(几千台)上运行,我注意到这是一个主要的原因内存泄漏。
我运行了 ANTS 内存分析器,它告诉我,我的大部分内存都是由字符串占用的,这些字符串包含诸如“% Idle Time”、“处理器信息”和“Cache Faults/sec”之类的 strage 值。我已经认识到这些字符串可能是程序中性能计数器的一部分,问题是我在程序中没有任何性能计数器。
深入挖掘发现这些字符串保存在 PerformanceCounterLib 保存的哈希表中,这些哈希表由存储在 PerformanceCounterLib 类的内部静态成员(其本身是内部的)中的另一个哈希表保存。
更深入地挖掘兔子洞,我发现 Process.GetProcesesByName 使用 PerformanceCounterLib 来获取在远程计算机上运行的进程列表,并且对于每台远程计算机,在静态内部变量中创建和引用另一个 PerformanceCounterLib 实例PerformanceCounterLib 的。这些实例中的每一个都包含我发现的字符串哈希表阻塞了我的内存(每个实例都在 300-700 kb 之间,这意味着它阻塞了我的大对象堆)。
我没有找到删除那些未使用的 PerformanceCounterLib 实例的方法,它们都是内部的,用户无权访问它们。
如何解决我的内存问题?这真的很糟糕,我的程序在 24 小时内达到了 5GB(我的服务器的限制)。
编辑:添加了一段代码(未经测试),应该会重现问题。澄清:
/// computerNames is a list of computers that you have access to
public List<string> GetProcessesOnAllComputers(List<string> computerNames)
{
var result = new List<string>();
foreach(string compName in computernames)
{
Process[] processes = Process.GetProcesses(compName); // Happens with every method that gets processes on a remote computer
string processString = processes.Aggregate(new StringBuilder(), (sb,s) => sb.Append(';').Append(s), sb => sb.ToString());
result.Add(processString);
foreach (var p in processes)
{
p.Close();
p.Dispose();
}
processes = null;
}
}
【问题讨论】:
-
如果我理解正确的话,每台远程机器会额外占用 300-700 kB(我猜是字节而不是位?),并且您无论如何都不能释放它,因为它由 Process 静态类持有?您是否尝试过使用
EnterDebugMode和LeaveDebugMode方法?我正在考虑以某种方式“重置” Process 静态类。但正如上面的评论所说,也要提交一个错误,因为这显然不是想要的行为。 -
很容易错过 Process.Dispose() 方法。从技术上讲,也可以使用 Process 类做很多工作,并且永远不会消耗足够的 GC 堆来触发收集。终结器线程上的死锁是一个笨蛋。先从那里开始,看看 .NET 性能计数器(呵呵)。
-
@HansPassant 我肯定已经关闭并处理了进程数组。这是我注意到代码中的第一件事是错误的,它可能确实减轻了负载,但事实并非如此。
标签: c# performance memory memory-leaks profiling