Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

CPUAlert: Save your CPU from Burning Hot and Battery Running Out Quickly

0.00/5 (No votes)
11 Jun 2011 5  
CPUAlert monitors CPU and Memory consumption of processes and alerts you when they are taking too much consistently and gives you an option to recycle or terminate
CPU Alert popup

Introduction

If your computer is running hot or battery is running out quickly, then it is most likely due to some application or process consuming high CPU or memory. If you keep running applications for a long time, for example, Outlook, then it continues to grow in memory consumption and does not free up memory efficiently. As a result, your computer runs out of physical memory and other applications run slower. Sometimes Outlook, browser, image editing applications or some other applications start taking full CPU as they get into some infinite loop and make your CPU hot and you get sluggish performance from your application.

CPUAlert is an application that monitors CPU and memory consumption of applications and alerts you if some application is consistently taking high CPU or high memory. It will not only save your CPU and battery’s lifetime but also make your computer run smoothly and let your active applications run as fast as possible.

How to Use the Application

While it is running, if some process is consuming more than 200 MB memory, it will show you an alert:

image

Here you can see that my Outlook is taking 244 MB of physical RAM.

You can either postpone the alert for 5 mins (just press ESC), or ignore the process permanently so that you no longer receive alerts for the process anymore, or you can close it and reclaim memory.

The handy feature is “Restart” which closes the application and starts again. This generally frees up memory that clogs up the process.

The same alert will come up if some process is consuming more than 30% CPU for over 5 mins.

You can configure all these settings like what’s the tolerable limit for CPU and memory, how frequently to show the alert, how long to wait before closing an application, etc. by right clicking on the Task bar icon and choosing Settings.

image

image

The application registers itself to start at Windows startup. If you want to remove it from Windows Startup, just delete the application shortcut from Start Menu->All Programs->Startup menu.

Using the Code

CPU Alert is a small project. The important files are:

  • MonitorCPUForm.cs: This is the main window which is loaded when the app starts. It hides itself in the system tray and keeps the app running in background. It also hosts a "Settings" view which lets you change the CPU and Memory threshold, alert interval, etc. It also orchestrates the operations and shows the necessary alerts.
  • KillProcessForm.cs: It has just the UI for the alert that you see in the above popup. No intelligence.
  • Monitor.cs: This is the important class that does the monitoring, measurement of CPU and Memory usage and raises the alert. If you want to have this kind of feature in your application, you can reuse this class.

Let's look at the Monitor.cs class first which has the brainpower to monitor CPU and memory. It uses WMI to monitor the processes. I tried using Process class first and tried to access the TotalProcessorTime but it gave me "Access Denied" for processes like mysqld. For some processes, I did not get the WorkingSet and so on. Then I tried using PerformanceCounter to get the CPU and Memory usage of process. It was too expensive to query Performance Counter for each process. So, I had to rely on WMI to give me the information.

private ManagementObjectSearcher _Searcher = 
            new ManagementObjectSearcher("root\\CIMV2",
            "SELECT IDProcess, Name, PercentProcessorTime, Description, 
	   WorkingSet FROM Win32_PerfFormattedData_PerfProc_Process");
.
.
.
private List<ProcessInfo> GetUsage()
{
    var processes = new List<ProcessInfo>();
    foreach (ManagementObject queryObj in _Searcher.Get())
    {
        var process = new ProcessInfo
        {
            Id = Convert.ToInt32(queryObj["IDProcess"]),
            Name = Convert.ToString(queryObj["Name"]),
            CpuUsage = Convert.ToInt32(queryObj["PercentProcessorTime"]),
            Description = Convert.ToString(queryObj["Description"]),
            WorkingSet = Convert.ToInt64(queryObj["WorkingSet"]),
        };

        if (string.IsNullOrEmpty(process.Description))
            process.Title = process.Name;
        else
            process.Title = process.Description;

        if (process.Id > 0)
            processes.Add(process);
    }

    return processes;
}  

Pretty straight forward. The only cool thing to learn here is I declared the ManageObjectSearcher as a private variable, not a local variable to this function because it seems creating and disposing it every time the GetUsage function is called is very expensive. CPU was getting 20% for about 30 seconds on WMIpserv.exe whenever I tried to create and dispose the searcher.

Closing a process turned out to be a R&D challenge. You can kill a process by calling Process.Kill, but that terminates the process abnormally. If you are killing Outlook this way, it causes data corruption. So, you have to gracefully close the application. However, if some app is stuck and taking 100% CPU, trying to close it gracefully does not work. You have to take drastic measures. In that case, killing it is only the solution.

So, first I try to close the process gently:

/// <summary>
/// Closes the process by sending Shutdown message to the main window. 
// Then it starts a timer to check if the process is really closed. 
/// </summary>
/// <param name="processToKill">The process to kill.</param>
private void CloseProcess(ProcessInfo processToKill)
{
    try
    {
        Process process = Process.GetProcessById(processToKill.Id);
        processToKill.Path = process.MainModule.FileName;
        process.CloseMainWindow();
    }
    catch (Exception closeException)
    {
        if (MessageBox.Show(this, "Unable to close process: " 
            + (processToKill.Title) + Environment.NewLine
            + closeException.Message + Environment.NewLine
            + "Do you want to kill it?", "Unable to close process", 
            MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
        {
            KillProcess(processToKill);
        }
    }

    // Check back after a while if the process is still running. 
    //If it's running kill it
    CheckAfterAWhileIfItsStillRunning(processToKill);
} 

Here it calls the Process.CloseMainWindow function to send a shutdown message to the process. If the process has a visual window, it will receive the message and hopefully it will close itself soon. If there's no visual window, then it will not receive the message and it will not shutdown. So, we will have to kill it.

Even if some process receives the shutdown message, it may not terminate properly if it's stuck in some infinite loop. In that case, we need to check back after a while if it's still running and then kill it.

private void KillProcess(ProcessInfo processToKill)
{
try
{
	foreach (Process process in Process.GetProcessesByName(processToKill.Name))
	{
		// Check if the process is still running
		if (process.Id == processToKill.Id)
		{
			if (!process.HasExited)
			{
				process.Kill();
				process.Close();
			}
		}
	}

	// If user has requested the process to be restarted, then restart it.
	if (processToKill.CanRestart)
		Process.Start(processToKill.Path);

}
catch (Exception killException)
{
	MessageBox.Show(this, "Unable to kill process: " 
	+ (processToKill.Title)
	+ Environment.NewLine + "You should restart your computer", 
	"Unable to kill process",
	MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}

This function is fired from a timer. We wait for a while and then go for the kill. First check, if the process is still lying around. If it is, then kill it.

Another interesting topic is memory usage. As you know, Windows Forms apps when started, take up about 10 MB to 15 MB memory. It's too much for a utility which is running in the background and trying to save you some memory. It's because when .NET WinForms libraries load, they load a lot of stuff hoping you would need it and it would save additional load time. So, I had to force .NET to flush out any unused stuff. I have a MemoryHelper which does this:

static class MemoryHelper
{
	[DllImport("psapi.dll")]
	static extern int EmptyWorkingSet(IntPtr hwProc);

	public static void ClearMemory()
	{
		try
		{
			GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
		   
			EmptyWorkingSet(Process.GetCurrentProcess().Handle);
		}
		catch
		{
		}
	}
} 

First it forces a Garbage Collection, then it calls the EmptyWorkingSet function on psapi.dll to flush out the working set, which is the physical memory allocation. I don't know whether or not it works, but at least Taskbar shows that the app now takes only 5 MB memory. Super!

Conclusion

Hope CPUAlert will save you from slowing down your PC or burning out laptop batteries quickly. If you find it useful, please spread the love. Start by voting for this article.

History

  • 2nd March, 2010: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here