How to solve memory leak caused by System.Diagnostics.PerformanceCounter
Summary
I have written a process monitor command-line application that takes as parameters:
- The process name or process ID
- A CPU Threshold percent.
What the program does, is watches all processes with the passed name or pid, and if their CPU usage gets over the threshold%, it kills them.
I have two classes:
ProcessMonitor
and ProcessMonitorList
The former, wraps around System.Diagnostics.PerformanceCounter
IEnumarable
that allows a list-like structure of the former.
The problem
The program itself works fine, however if I watch the Memory Usage on Task Manager, it grows in increments of about 20kB per second. Note: the program polls the CPU counter through PerformanceCounter
every second.
This program needs to be running on a heavily used server, and there are a great number of processes it is watching. (20-30).
Investigation So far
I have used PerfMon to monitor the Private Bytes of the process versus the Total number of Bytes in all Heaps and according to the logic presented in the article referenced below, my results indicate that while fluctuating, the value remains bounded within an acceptable range, and hence there is no memory leak:
ArticleI have also used FxCop to analyze my code, and it did not come up with anything relevant.
开发者_StackOverflow社区The Plot Thickens
Not being comfortable with just saying, Oh then there's no memory leak, I investigated further, and found (through debugging) that the following lines of code demonstrate where the leak is occurring, with the arrow showing the exact line.
_pc = new PerformanceCounter("Process", "% Processor Time", processName);
The above is where _pc is initiated, and is in the constructor of my ProcessMonitor
class.
The below is the method that is causing the memory leak. This method is being called every second from my main.
public float NextValue()
{
if (HasExited()) return PROCESS_ENDED;
if (_pc != null)
{
_lastSample = _pc.NextValue(); //<-----------------------
return _lastSample;
}
else return -1;
}
This indicates to me that the leak exists inside the NextValue()
method, which is inside the System.Diagnostics.PerformanceCounter class.
My Questions:
- Is this a known problem, and how do I get around it?
- Is my assumption that the task manager's memory usage increasing implies that there is indeed a memory leak correct?
- Are there any better ways to monitor multiple instances of a specific process and shut them down if they go over a specific threshold CPU usage, and then send an email?
So I think I figured it out.
Using the Reflector tool, I was able to examine the code inside System.Diagnostics
.
It appears that the NextValue
method calls
GC.SuppressFinalization();
This means that (I think, and please correct if I am wrong) that I needed to explicitly call Dispose()
on all my classes.
So, what I did is implement IDisposable
on all of my classes, especially the one that wrapped around PerformanceCounter
.
I wrote more explicit cleanup of my IList<PerformanceMonitor>
, and the internals,
and voilà, the memory behavior changed.
It oscillates, but the memory usage is clearly bounded between an acceptable range over a long period of time.
精彩评论