Does the CLR release memory when no longer needed?

A colleague asked the other day if the CLR releases memory when it’s no longer needed.

Suppose you allocate lots of memory, then release it. The CLR will grow the managed heap (the green below), but it will also shrink it if it can.

All memory in a process must come from a pool of virtual addresses, obtained by calling VirtualAlloc. For a 32 bit process, these addresses range from 0 to 2^32, about 4 billion. The memory is allocated in chunks or segments as needed. These segments must be a multiple of 64K in size. Therefore, there can only be a maximum of 64k chunks in a 32 bit process because 64k times 64k = 4 gig. (64k = 2^16, and 2^16 * 2 ^ 16 = 2^32)

If you VirtualAlloc only 10 bytes, 64k is used, most of which is wasted Virtual Address space. If you need 64k + 1, then 128k is used.

For smaller memory needs, heaps are used. Heaps call VirtualAlloc for big chunks of memory, then suballocate it for callers.

An address range can be committed, which means it will be mapped to physical memory, and then you can read/write to it. Committing is done in 4k pages, which can be “paged” in/out of physical memory, using a “paging” file.

When the CLR needs more memory to satisfy a program, it needs to call VirtualAlloc to grow the GC Heap.

When the memory is no longer needed, the memory can be freed. However, there are situations where the memory cannot be freed.

Run the program below to create a class that uses a lot of memory. Then it frees it. Watch the memory consumption with your favorite memory tool, such as Task Manager, Process Explorer, VMMap

When the Dispose is called, the reference to the array is set to null, a Garbage collection is invoked, and the memory is freed.

Observe the various kinds of memory, Private, Managed, Working Set.

Start Visual Studio (most recent versions will work)

File->New->Project->C# Windows WPF Application

Replace Mainwindow.Xaml.cs with the code below

Hit Ctrl-F5 to run the program.

Exercises:

· What happens if you create some object that pins memory (like creating a FileSystemWatcher ) (see the Fixed statement in C# )

· What happens if you create and lock an object in the middle of one of the segments?

Here are some screen shots of various kinds of memory: observe the green “Managed” rows, which are the VirtualAlloc calls made by the CLR.

See also:

https://blogs.msdn.com/b/calvin_hsia/archive/tags/memory/

Use Perfmon to analyze your managed memory

Process Explorer and Process Monitor can help you understand a program

Start

clip_image002[4]

Allocated

clip_image004[4]

Disposed

clip_image006[4]

<code>

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += (o, e) =>
            {
                try
                {
                    var args = Environment.GetCommandLineArgs();
                    int num = 1000000;
                    int len = 100;

                    if (args.Length > 1)
                    {
                        num = int.Parse(args[1]);
                    }
                    if (args.Length > 2)
                    {
                        len = int.Parse(args[2]);
                    }
                    MsgBox.Show(string.Format("Start {0} {1}", num, len));
                    var somestr = "something";
                    using (var x = new BigClass(num, len))
                    {
                        MsgBox.Show(string.Format("hi {0}", x.strArray[32]));
                        somestr = new string('a', 200);
                    }
                    MsgBox.Show(string.Format("Released"));
                    Environment.Exit(0);

                }
                catch (Exception ex)
                {
                    this.Content = ex.ToString();
                }
            };
        }
    }
    public class MsgBox : Window
    {
        public static void Show(string str)
        {
            var x = new MsgBox(str);
            x.ShowDialog();
        }
        public MsgBox(string str)
        {
            Width = 600;
            Height = 100;
            var sp = new StackPanel()
            {
                Orientation = Orientation.Vertical
            };
            sp.Children.Add(new TextBlock()
            {
                Text = str
            });
            var btn = new Button()
            {
                Content = "Ok"
            };
            btn.Click += (oBtn, ebtn) =>
            {
                this.Close();
            };
            sp.Children.Add(btn);
            this.Content = sp;
        }
    }

    class BigClass : IDisposable
    {
        public string[] strArray;
        public BigClass(int num, int len)
        {
            strArray = new string[num];
            for (int i = 0; i < strArray.Length; i++)
            {
                strArray[i] = string.Format(
                    "foobar lets take up some memory {0,-7:n0}", i) +
                    new string('a', len); // repeat 'a' len times 
            }
        }

        public void Dispose()
        {
            strArray = null;
            GC.Collect();
            MsgBox.Show("Dispose");
        }
    }
}

 

</code>