10

我有一段代码使用静态方法Process.GetProcessesByName(String, String)获取远程计算机上的进程列表,它在很多计算机(数千台)上运行,我注意到这是导致重大的内存泄漏。

我运行了 ANTS 内存分析器,它告诉我,我的大部分内存都是由字符串占用的,字符串包含诸如“% Idle Time”、“Processor Information”和“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;
    }
}
4

3 回答 3

3

您可以调用PerformanceCounter.CloseSharedResources

在内部, this 调用PerformanceCounterLib.CloseAllLibraries,这听起来像。

我建议确保你在没有调用的时候调用GetProcessesByName它,因为看起来里面可能有一些PerformanceCounterLib你不想激起的竞争条件。

即有一个名为的共享变量libraryTable被检查一次,然后假定它在一种方法中继续有效,但可能随时被清除CloseAllLibraries- 所以它绝对不是线程安全的。

于 2012-10-26T10:48:36.373 回答
0

I was inspecting with ILSpy and analyzed the method call stack of your method. You are right, there are a static hashtable. I suggest: you should call in the PerformanceCounter class the following method:

// 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();
}

That calls the PerformanceCounterLib.CloseAllLibraries(); which disposes all used hashtable.

于 2012-10-26T18:16:43.470 回答
0

警告:这只是一个非常肮脏的快速修复,但使用反射来杀死它们。

访问私有变量: 我可以使用反射更改 C# 中的私有只读字段吗?

使用静态类的示例: 在对象初始化之前使用反射设置静态变量值?

您可以使用的变体typeof(Process).GetFields(BindingFlags.Static | BindingFlags.NonPublic)来查找字段等。

我相信快速修复是值得的,因为 的行为Process显然是不正确的。

于 2012-10-26T10:17:16.633 回答