我有一段代码使用静态方法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<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;
}
}
答案 0 :(得分:3)
您可以拨打PerformanceCounter.CloseSharedResources。
在内部,这会调用PerformanceCounterLib.CloseAllLibraries
,这听起来像是听起来像。
我建议您确保在没有拨打GetProcessesByName
的情况下拨打电话,因为看起来PerformanceCounterLib
内部可能存在某些竞争条件想要挑衅。
即。有一个名为libraryTable
的共享变量,它被检查一次,然后假设在一个方法中继续有效,但可能在任何时候被CloseAllLibraries
清除 - 所以它绝对不是线程安全的。
答案 1 :(得分:0)
警告:这只是一个非常脏的quickfix,但是使用反射来消除它。
访问私有变量: Can I change a private readonly field in C# using reflection?
使用静态类的示例: Using Reflection to set a static variable value before object's initialization?
您可以使用typeof(Process).GetFields(BindingFlags.Static | BindingFlags.NonPublic)
的变体来查找字段等。
我认为由于Process
的行为显然不正确,因此快速解决问题已被证实。
答案 2 :(得分:0)
我正在检查ILSpy并分析了方法的方法调用堆栈。
你是对的,有一个静态哈希表。
我建议:你应该在PerformanceCounter
类中调用以下方法:
// System.Diagnostics.PerformanceCounter
/// <summary>Frees the performance counter library shared state allocated by the counters.</summary>
/// <filterpriority>2</filterpriority>
/// <PermissionSet>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1">
/// <Machine name=".">
/// <Category name="*" access="Browse" />
/// </Machine>
/// </IPermission>
/// </PermissionSet>
public static void CloseSharedResources()
{
PerformanceCounterPermission performanceCounterPermission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Browse, ".", "*");
performanceCounterPermission.Demand();
PerformanceCounterLib.CloseAllLibraries();
}
调用PerformanceCounterLib.CloseAllLibraries();
来处理所有使用过的哈希表。