Using multiple heaps efficiently
You can inspect the processes that run on your machine using Task Manager (Ctrl-Shift-Escape) or , Process Explorer from Sysinternals
Or you can write your own: here’s source code: What is your computer doing with all that memory? Write your own memory browser (if you’re using Dev10, just change “Class Window1” to “Class MainWindow”)
These processes use memory mainly via Heaps. What happens if a heap allocates a whole bunch of memory, and then frees it all? Last time, in Managed code using unmanaged memory: HeapCreate, Peek and Poke, I showed code that allowed you to create and manipulate Heaps in VB.Net and C#.
The pattern of heap usage, lots of small allocations or a few large ones, make a big difference.
Below is sample that allocates 1 Gig via 1024 1 Meg allocations or 1 million 1024 byte allocations. You’ll see vastly different behavior with each.
Start Visual Studio ->File->New Project->VB/C#->Windows->WPF Application, switch to the MainWindow.Xaml.vb/cs window and paste the appropriate version below. Set a breakpoint (F9), then single step and observe the code as you use the memory inspection tool. Use Set Next Statement to modify the code behavior. Change parameters and values as you single step.
If you use ProcessExplorer, watch the VirtualSize column to see that freeing all the individual allocations within a heap may not free all the virtual address space in the heap, depending on the heap usage patterns.
Using your own memory browser (from above) you can see the individual Virtual Allocs.
Internally a heap needs to use memory to track the allocations: probably more internal memory (and virtual address space) for more allocations. Even freeing the allocations may not free these internal structures. HeapDestroy will.
The moral? Even if you free memory in a heap you’re probably not getting back everything: try to combine the heaps you use.
So, for example, you start VS, work in VB for a while, then close VB and work in C# or C++ for a while. If the VB heap isn’t destroyed, it’s just taking up valuable memory. Consider using the Process Heap
See also:
Inspect your memory image and see fragmentation
Sample output:
0 Hnd= 06f10000 #Alloc=1,048,576 Tot=1,073,741,824 (40000000)
1 Hnd= 04d30000 #Alloc=715,219 Tot=732,384,256 (2ba74c00)
heap create failed
2 Hnd= 00000000 #Alloc=0 Tot=0 (00000000)
heap create failed
3 Hnd= 00000000 #Alloc=0 Tot=0 (00000000)
<VBCode>
Option Strict On
Imports System.Runtime.InteropServices
Class MainWindow
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
Dim results As New List(Of heapData)
For i = 0 To 3
Dim hData = HeapCreateAndFill(1024 * 1024, 1024) ' alloc 1 gig=1Million * 1K
'Dim hData = HeapCreateAndFill(1024, 1024 * 1024) ' alloc 1 gig=1K * 1Meg
Debug.WriteLine(String.Format("{0} {1}", i, hData))
' MsgBox(String.Format("{0} {1}", i, hData))
hData.FreeAll() ' we free all allocs but not the heap itself
results.Add(hData)
Next
End Sub
Function HeapCreateAndFill(ByVal nTimes As Integer, ByVal nSize As UInteger) As heapData
Dim hData As New heapData
' hData.hHandle = Heap.GetProcessHeap ' Heap.HeapCreate(0, 0, 0)
hData.hHandle = Heap.HeapCreate(0, 0, 0)
If hData.hHandle = IntPtr.Zero Then
Debug.WriteLine("heap create failed")
Else
For i = 0 To nTimes - 1
Dim ptr = Heap.HeapAlloc(hData.hHandle, 0, nSize)
If ptr = IntPtr.Zero Then ' did we run out of mem?
Exit For
End If
hData.TotAlloc += nSize
hData.nAllocs += 1
hData.ptrs.Add(ptr)
Next
End If
Return hData
End Function
Class heapData
Public hHandle As IntPtr
Public TotAlloc As Int64
Public nAllocs As Integer
Public ptrs As New List(Of IntPtr)
Public Sub FreeAll()
For Each ptr In ptrs ' free every allocation
Heap.HeapFree(hHandle, 0, ptr)
Next
End Sub
Public Overrides Function ToString() As String
Return String.Format("Hnd= {0:x8} #Alloc={1:n0} Tot={2:n0} ({3:x8})", hHandle.ToInt32, nAllocs, TotAlloc, TotAlloc)
End Function
End Class
End Class
Public Class Heap
<DllImport("kernel32.dll", SetLastError:=True)> _
Public Shared Function HeapCreate(
ByVal flOptions As HeapFlags,
ByVal dwInitialSize As UInteger,
ByVal dwMaximumSize As UInteger
) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True)>
Public Shared Function HeapAlloc(
ByVal hHeap As IntPtr,
ByVal dwFlags As HeapFlags,
ByVal dwSize As UInteger
) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True)>
Public Shared Function HeapFree(
ByVal hHeap As IntPtr,
ByVal dwFlags As HeapFlags,
ByVal lpMem As IntPtr
) As Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True)>
Public Shared Function HeapDestroy(
ByVal hHeap As IntPtr
) As Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True)> _
Public Shared Function GetProcessHeap(
) As IntPtr
End Function
<Flags()>
Public Enum HeapFlags
HEAP_NO_SERIALIZE = &H1
HEAP_GENERATE_EXCEPTIONS = &H4
HEAP_ZERO_MEMORY = &H8
End Enum
End Class
</VBCode>
<C# Code>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Heapcs
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var results = new List<heapData>();
for (var i = 0; i <= 3; i++)
{
var hData = HeapCreateAndFill(1024 * 1024, 1024);
Debug.WriteLine(String.Format("{0} {1}", i, hData));
hData.FreeAll();
results.Add(hData);
}
}
heapData HeapCreateAndFill(int nTimes, uint nSize)
{
var hData = new heapData();
hData.hHandle = Heap.HeapCreate(0, 0, 0);
if (hData.hHandle == IntPtr.Zero)
{
Debug.WriteLine("heap create filed");
}
else
{
for (var i = 0; i < nTimes; i++)
{
var ptr = Heap.HeapAlloc(hData.hHandle, 0, nSize);
if (ptr == IntPtr.Zero)
{
break;
}
hData.TotAlloc += nSize;
hData.nAllocs++;
hData.ptrs.Add(ptr);
}
}
return hData;
}
class heapData
{
public IntPtr hHandle;
public Int64 TotAlloc;
public int nAllocs;
public List<IntPtr> ptrs = new List<IntPtr>();
public void FreeAll()
{
foreach (IntPtr ptr in ptrs)
{
Heap.HeapFree(hHandle, 0, ptr);
}
}
public override string ToString()
{
return String.Format("Hnd= {0:x8} #Alloc={1:n0} Tot={2:n0} ({3:x8})", hHandle.ToInt32(), nAllocs, TotAlloc, TotAlloc);
}
}
}
public class Heap
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr HeapCreate(HeapFlags flOptions, uint dwInitialsize, uint dwMaximumSize);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr HeapAlloc(IntPtr hHeap, HeapFlags dwFlags, uint dwSize);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool HeapFree(IntPtr hHeap, HeapFlags dwFlags, IntPtr lpMem);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool HeapDestroy(IntPtr hHeap);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetProcessHeap();
[Flags()]
public enum HeapFlags
{
HEAP_NO_SERIALIZE = 0x1,
HEAP_GENERATE_EXCEPTIONS = 0x4,
HEAP_ZERO_MEMORY = 0x8
}
}
}
</C# Code>
Comments
- Anonymous
July 01, 2010
Win32 has an API to flush pages out of memory for an entire process. Is there any chance we can see that in .NET or better yet, in the task manager?