How to monitor and respond to memory use
A colleague asked me how to run code in response to low memory condition.
Apparently, data is buffered and can be flushed to disk or a server when memory gets low.
So I showed him the code below.
Start Visual Studio
File->New->Project->C# WPF application
Paste in the code below in the MainWindow.Xaml.cs file
Then hit F5 to run it.
The sample runs some code in a background thread that:
1. Gets the performance counters for the current process. There may be a delay in getting the counters so it checks for Null
2. Gets the values of the counters
3. Updates the textbox with the values
4. Allocates a large string and adds it to a list of strings (consuming large amounts of memory)
5. Sleeps for 1 second
6. Repeats
Eventually, the program runs out of memory
See also
Use Perfmon to analyze your managed memory
<code sample>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += (ol, el) =>
{
try
{
var sp = new StackPanel() { Orientation = Orientation.Vertical };
this.Content = sp;
var txtbox = new TextBox() { Text = "hi" };
var btn = new Button() { Content = "Hit me" };
sp.Children.Add(btn);
sp.Children.Add(txtbox);
bool fDone = false;
btn.Click += (ob, eb) =>
{
fDone = true;
};
ThreadPool.QueueUserWorkItem((p) =>
{
var start = DateTime.Now;
var pid = Process.GetCurrentProcess().Id;
PerformanceCounter perfCounterGCBytes = null;
PerformanceCounter perfCounterPrivateBytes = null;
PerformanceCounter perfCounterVirtualBytes = null;
var listOfBigStuff = new List<string>();
while (!fDone)
{
//update the text box from a different
//thread by telling it's dispatcher to do it
txtbox.Dispatcher.Invoke(() =>
{
try
{
if (perfCounterGCBytes == null)
{
perfCounterGCBytes = GetPerfCounter(
".NET CLR Memory",
"# Bytes in all Heaps",
"Process ID",
pid);
perfCounterPrivateBytes = GetPerfCounter(
"Process",
"Private Bytes",
"ID Process",
pid);
perfCounterVirtualBytes = GetPerfCounter(
"Process",
"Virtual Bytes",
"ID Process",
pid);
}
if (perfCounterGCBytes != null)
{
var tspan = DateTime.Now - start;
var gcBytes = (int)perfCounterGCBytes.NextValue();
var privBytes = (int)perfCounterPrivateBytes.NextValue();
var virtBytes = (int)perfCounterVirtualBytes.NextValue();
txtbox.Text = string.Format(
"{0,13:n0} GC={1,-13:n0} priv={2,-13:n0} virt={3,-13:n0} #={4:n0}",
tspan.TotalMilliseconds,
(int)perfCounterGCBytes.NextValue(),
(int)perfCounterPrivateBytes.NextValue(),
(int)perfCounterVirtualBytes.NextValue(),
listOfBigStuff.Count
);
// do something to use lots of mem
for (int i = 0; i < 10000; i++)
{
listOfBigStuff.Add(new string('a', 10000));
}
if (gcBytes> 100000)
{
// threshold: free some mem
}
}
}
catch (Exception ex)
{
txtbox.Text = ex.ToString();
// free some mem
listOfBigStuff.Clear();
}
});
Thread.Sleep(1000);
}
});
}
catch (Exception ex)
{
this.Content = ex.ToString();
}
};
}
public PerformanceCounter GetPerfCounter(string perfcountCat, string perfcountName, string pidstr, int vspid)
{
PerformanceCounter pc = null;
var cat = new PerformanceCounterCategory(perfcountCat);
foreach (var inst in cat.GetInstanceNames()) // exception if you're not admin or "Performance Monitor Users" group (must re-login)
{
// LogString("Perf count cat = {0} Name = {1}", perfcountCat, inst);
using (var cntr = new PerformanceCounter(cat.CategoryName, pidstr, inst, true))
{
try
{
var val = (int)cntr.NextValue();
if (val == vspid)
{
pc = new PerformanceCounter(perfcountCat, perfcountName, inst);
break;
}
}
catch (Exception)
{
// System.InvalidOperationException: Instance 'IntelliTrace' does not exist in the specified Category.
// LogString("Exception {0}", ex.ToString());
}
}
}
return pc;
}
}
}
</code sample>