Hi,
I'm developing a small in-house application to monitor the performance such as CPU usage, available memory, disk spaceour etc. I start with a small test settig up the CPU usage monitoring.
I am reading the CPU% once per second using WMI API function and then produce an average value each minute.
The problem with this is I get some memory leakage while looking at the process in the Taskmanager.
I've been googling this but all I found was a hotfix concerning leak in Win Server 2008 while I'm on XP/Win Server 2003 machines.
So, there's anyone here that could provide a working solution based on my code input, I'd be more than pleased!
Code:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.IO;using System.Runtime.InteropServices;using System.Data;using System.Data.SqlClient;using System.Diagnostics;using System.Management;using System.Configuration;using System.Threading;namespace ServerMonitoringConsole{ class Program { static string clientName = "myName"; static string clientPwd = "myPwd"; static string domain = "ntlmdomain:myDomain"; static List<int> cpuLoads = new List<int>(); static string errorStr = ""; static void Main(string[] args) { int load = 0; int avg = 0; long mem = 0; int cpuTimePeriod = 60; string[] stringServers = { "serv1", "serv2" }; ConnectionOptions co = new ConnectionOptions(); co.Username = clientName; co.Password = clientPwd; co.Authority = domain; List<ManagementScope> msList = new List<ManagementScope>(); msList.Add(new ManagementScope(@"\\" + stringServers[0] + @"\root\cimv2", co)); msList.Add(new ManagementScope(@"\\" + stringServers[1] + @"\root\cimv2", co)); ObjectQuery sq = new ObjectQuery("SELECT * FROM Win32_Processor"); List<ManagementObjectSearcher> mosList = new List<ManagementObjectSearcher>(); foreach (ManagementScope ms in msList) { ms.Connect(); mosList.Add(new ManagementObjectSearcher(ms, sq)); } while (true) { load = 0; int counter = 0; while (cpuLoads.Count <= cpuTimePeriod) { Thread.Sleep(1000); foreach (string server in stringServers) { GetCPUPerformance(server, ref load, mosList[counter]); cpuLoads.Add(load); counter++; } } avg = (int)cpuLoads.Sum() / cpuTimePeriod; cpuLoads.Clear(); } } static void GetCPUPerformance(string computerName, ref int cpuLoad, ManagementObjectSearcher mos) { using (ManagementObjectCollection moObjs = mos.Get()) { foreach (ManagementObject mo in moObjs) { try { cpuLoad = Convert.ToInt32(mo["LoadPercentage"]); } finally { mo.Dispose(); } } } } }}
Thanks I have already tried that with no visual diffence. There are also several threads saying that you should not call this function manually since you then can mess up the OS thread scheduling/handeling schemas.
Once again, I did have GC.Collect() at the end on all methods but with no effect at all, i.e. still memory leaking on every call (one per second).
Ok, but I don't see how ensuring a synchronous thread creation should prevent memory leak?
Interesting to read about performance but this is not the main issue here, however I did try to move out the creation of the objects before entering the main loop which would be something like scenario #3
The connections are not closed since I reuse them according to your link (there's only 2 connections created in this example, one for each machine to monitor)
My Console app is running on a XP machine Querying Win 2003 and Win XP machines. No noticable difference in the memory leak between the machines.
This Hotfix says "a memory leak of up to 20 bytes may occur for each WQL query to the decoupled WMI provider" while I have 20 KB / Query. Since I need to query several of our intranet machines (~ 40) every second, the machine hosting the Console app will quickly stop working normally so ...
Is there only one expert here in this area? :/
Ron Malmstead
I posted a request for assistance to the moderator to see if we can get some additional experts to look at this.
peer754
ASKER
Thanks!
I've also been trying to find a work-around to prevent from querying too frequently as now now, every second.
I don't see how though regarding the CPU usage if you're not fine with taking a snapshot every minute or so.
If there was a way to get history CPU usage in one query / minute I'm done. Since I then could run my app as a scheduled task and hence, killing the thread and free the memory through OS normal scheduling.
the above code accesses 'mosList' at index 'counter' though counter obviously was independent of the size of the list.
i would have assumed that an exception was thrown when counter is equal or greater to size but in case it grows the list instead it could be a reason for the growing memory consumption.
in my opinion task manager is not suitable to detect leakage cause both the heap manager and the CLR garbage collector would reserve some of the freed memory for efficience and optimization reasons what means that task manager's report is not accurate.
counter is not an issue here I'm afraid, it's only used to increment the objects corresponding to each server I want to monitor.
I have simplyfied the code only to one machine and one query (the CPU usage) + I have also tried querying the local machine to exclude the RPC from the error search.
Still, there's a leak according to, yes, the Task manager. I know looking at the Task manager won't give very accurate result in precise memory leak numbers but it still gives a hint that something's wrong.
In my last test on my local machine, I ran the code for about 15 minutes and the thread's memory increased to the double compare to when it started.
Here's my new simplyfied code:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.IO;using System.Runtime.InteropServices;using System.Data;using System.Data.SqlClient;using System.Diagnostics;using System.Management;using System.Configuration;using System.Threading;namespace ServerMonitoringConsole{ class Program { static List<int> cpuLoads = new List<int>(); static void Main(string[] args) { int avg = 0; int cpuReadPeriod = 5000; //Read CPU load every 5 sec int cpuTimePeriod = 60000 / cpuReadPeriod; while (true) { while (cpuLoads.Count <= cpuTimePeriod) { Thread.Sleep(cpuReadPeriod); using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_Processor")) { foreach (ManagementObject queryObj in searcher.Get()) { try { Console.WriteLine("LoadPercentage: {0}", queryObj["LoadPercentage"]); cpuLoads.Add(Convert.ToInt32(queryObj["LoadPercentage"])); } finally { queryObj.Dispose(); } } } }; avg = (int)cpuLoads.Sum() / cpuTimePeriod; cpuLoads.Clear(); Console.WriteLine("-----------------------------------"); Console.WriteLine("Win32_Processor instance"); Console.WriteLine("-----------------------------------"); Console.WriteLine("Average CPU LOAD = " + avg.ToString() + "%."); GC.Collect(); } } }}