Process.GetProcessesByName(String,String)内存泄漏

时间:2012-10-26 09:48:22

标签: c# performance memory memory-leaks profiling

我有一段代码使用静态方法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;
    }
}

3 个答案:

答案 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();来处理所有使用过的哈希表。