Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
497 views
in Technique[技术] by (71.8m points)

c# - Process.GetProcessesByName(String, String) Memory Leak

I have a piece of code that gets a list of processes on a remote computer using the static method Process.GetProcessesByName(String, String), this runs on a lot of computers (a few thousands) and I've noticed it's a cause of a major memory leak.

I ran ANTS memory profiler which told me that most of my memory is taken by strings, strings containing strage values like "% Idle Time", "Processor Information", and "Cache Faults/sec". I've recognized those strings as probably being a part of Performance Counters in the program, the problem is I don't have any performance counters in the program.

Digging deeper found out those strings are held in hashtables that are held by PerformanceCounterLib which are held by ANOTHER hashtable that is stored inside an internal static member of the PerformanceCounterLib class (which in itself is internal).

Digging even deeper into the rabbit hole, I've found out that Process.GetProcesesByName uses PerformanceCounterLib to get the process list running on a distant computer and that for each remote computer another PerformanceCounterLib instance is created and referenced in the static internal variable of PerformanceCounterLib. Each of those instances hold that hashtable of strings that I found out is clogging my memory (each of them is between 300-700 kb, meaning it's clogging up my Large Object Heap).

I did not find a way to delete those unused PerformanceCounterLib instances, they are all internal and the user has no access to them.

How can I fix my memory problem? This is REALLY bad, my program hits 5GB (my server's limit) within 24 hours.

EDIT: added a piece of code (not tested) that should reproduce the problem. For clarification:

/// 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;
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can call PerformanceCounter.CloseSharedResources.

Internally, this calls PerformanceCounterLib.CloseAllLibraries, which does what it sounds like.

I'd advise making sure that you call this at a time when no calls to GetProcessesByName are in progress, since it looks like there may be some race conditions inside PerformanceCounterLib that you don't want to provoke.

i.e. there's a shared variable called libraryTable that is checked once then assumed to continue to be valid in one method, and yet might be cleared by CloseAllLibraries at any time - so its decidedly not thread safe.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...