Process.GetProcessesByName(String,String)内存泄漏
我有一段代码,使用静态方法Process.GetProcessesByName(String,String)获取远程计算机上的进程列表,这可以在很多计算机上运行(几千个),我注意到它的原因是主要的内存泄漏。
我运行了ANTS内存分析器,它告诉我,我的大部分内存都是字符串,字符串包含诸如“%Idle Time”,“Processor Information”和“Cache Faults / sec”之类的存储值。 我已经认识到这些字符串可能是程序中性能计数器的一部分,问题是我在程序中没有任何性能计数器。
深入挖掘发现这些字符串保存在由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 GetProcessesOnAllComputers(List computerNames) { var result = new List(); 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; } }
您可以调用PerformanceCounter.CloseSharedResources 。
在内部,它调用PerformanceCounterLib.CloseAllLibraries
,它听起来像它。
我建议你确保在没有调用GetProcessesByName
时候调用它,因为看起来PerformanceCounterLib
中可能存在一些你不想引发的竞争条件。
即,有一个名为libraryTable
的共享变量,它被检查一次然后假定在一个方法中继续有效,但可能随时被CloseAllLibraries
清除 – 所以它绝对不是线程安全的。
警告:这只是一个非常脏的quickfix,但使用reflection来消除它。
访问私有变量: 我可以使用reflection更改C#中的私有只读字段吗?
使用静态类的示例: 使用Reflection在对象初始化之前设置静态变量值?
您可以使用typeof(Process).GetFields(BindingFlags.Static | BindingFlags.NonPublic)
来查找字段等。
我相信快速解决方案会受到警告,因为Process
的行为显然不正确。
我正在检查ILSpy并分析了方法的方法调用堆栈。 你是对的,有一个静态哈希表。 我建议:你应该在PerformanceCounter
类中调用以下方法:
// System.Diagnostics.PerformanceCounter /// Frees the performance counter library shared state allocated by the counters. /// 2 /// /// /// /// /// /// /// public static void CloseSharedResources() { PerformanceCounterPermission performanceCounterPermission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Browse, ".", "*"); performanceCounterPermission.Demand(); PerformanceCounterLib.CloseAllLibraries(); }
它调用PerformanceCounterLib.CloseAllLibraries();
它处理所有使用过的哈希表。