Measure Memory Usage of Your Application
Scenario/Problem: | You need to find out how much memory your application is using. | Solution: | The GC class contains many handy memory-related methods, including GetTotalMemory(), which is the amount of memory the garbage collector thinks
is allocated to your application. The number may not be exactly right
because of objects that haven’t been garbage collected yet. However,
this has the advantage of being able to tell you about how much memory a
certain part of your program uses, rather than the entire process. |
long available = GC.GetTotalMemory(false);
Console.WriteLine("Before allocations: {0:N0}", available);
int allocSize = 40000000;
byte[] bigArray = new byte[allocSize];
available = GC.GetTotalMemory(false);
Console.WriteLine("After allocations: {0:N0}", available);
This same code prints the following:
Before allocations: 651,064
After allocations: 40,690,080
Get the OS View of Your Application’s Memory
You can also ask the operating system to give you information about your process:
//the Process class is in the System.Diagnostics namespace
Process proc = Process.GetCurrentProcess();
Console.WriteLine("Process Info: "+Environment.NewLine+
"Private Memory Size: {0:N0}"+Environment.NewLine +
"Virtual Memory Size: {1:N0}" + Environment.NewLine +
"Working Set Size: {2:N0}" + Environment.NewLine +
"Paged Memory Size: {3:N0}" + Environment.NewLine +
"Paged System Memory Size: {4:N0}" + Environment.NewLine +
"Non-paged System Memory Size: {5:N0}" + Environment.NewLine,
proc.PrivateMemorySize64,
proc.VirtualMemorySize64,
proc.WorkingSet64,
proc.PagedMemorySize64,
proc.PagedSystemMemorySize64,
proc.NonpagedSystemMemorySize64 );
Here’s the output:
Process Info:
Private Memory Size: 75,935,744
Virtual Memory Size: 590,348,288
Working Set Size: 29,364,224
Paged Memory Size: 75,935,744
Paged System Memory Size: 317,152
Non-paged System Memory Size: 37,388
What each of those numbers mean is not necessarily intuitive, and you should consult a good operating system book, such as Windows Internals (Microsoft Press), to learn more about how virtual memory works.
Note
You
can also use performance counters to track this and a lot of other
information about your application or its environment. You can access
these interactively from perfmon.exe, or see the MSDN documentation for
the PerformanceCounter class to see how you can incorporate these into your own applications. Clean Up Unmanaged Resources Using Finalization
Scenario/Problem: | You have a native resource (such as a kernel object) and need to ensure that it gets cleaned up by the garbage collector.
If for some reason
you want to manage raw file handles, bitmaps, memory-mapped files,
synchronization objects, or any other kernel object, you need to ensure
that the resource is cleaned up. Each of these resources is represented
by handles. To manipulate handles, you need to use native Windows
functions via P/Invoke | Solution: | Here’s an example of a class that manages a file handle with help from the .NET classes:
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Finalizer
{
public class MyWrappedResource
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
public static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
//IntPtr is used to represent OS handles
IntPtr _handle = IntPtr.Zero;
public MyWrappedResource(string filename)
{
_handle = CreateFile(filename,
0x80000000, //access read-only
1, //share-read
IntPtr.Zero,
3, //open existing
0,
IntPtr.Zero);
}
//Finalizers look like C++ destructors,
//but they are NOT deterministic
~MyWrappedResource()
{
//note: in real apps, don't put anything
//in finalizers that doesn't need to be there
Console.WriteLine("In Finalizer");
if (_handle != IntPtr.Zero)
{
CloseHandle(_handle);
}
}
public void Close()
{
if (_handle != IntPtr.Zero)
{
//we're already closed, so this object
//doesn't need to be finalized anymore
GC.SuppressFinalize(this);
CloseHandle(_handle);
}
}
}
}
|
When .NET detects a finalizer in a class, it ensures that it is called at some point in garbage collection.
Note
The
finalization phase of the collection process can be quite expensive, so
it’s important to use finalizers only when necessary. They should
almost never be used for managed resources—only unmanaged ones.
|