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>