Link to home
Start Free TrialLog in
Avatar of peer754
peer754

asked on

WMI API causes memory leak

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

Open in new window

SOLUTION
Avatar of Ron Malmstead
Ron Malmstead
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of peer754
peer754

ASKER

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.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of peer754

ASKER

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)
How about this hotfix for 2003?

http://support.microsoft.com/kb/933230
Avatar of peer754

ASKER

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? :/
I posted a request for assistance to the moderator to see if we can get some additional experts to look at this.
Avatar of 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.
It's a strange problem no doubt.

I'm wondering if the same query/program would cause the memory leak when ran directly on the machine versus remotely.
Avatar of peer754

ASKER

Exact same behaviour on local machine (XP SP3).
GetCPUPerformance(server, ref load, mosList[counter]);

Open in new window


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.

Sara
Avatar of peer754

ASKER

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

Open in new window

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I converted your latest code to vb and it seems to work without any issue.
I tested on Windows 7 and 2003
Avatar of peer754

ASKER

As suggested by one of the experts, removing some of the objects might have contributed to the final solution