Making a RAM Cleaner in .NET

Introduction

.NET and Windows are exceptionally good at managing memory, so you might ask: “Why am I reading this?” To answer your question: Sometimes you need to get rid of RAM in your program quickly, albeit that task is a bit dangerous. The fact of the matter is that you will never know what your applications will do with memory and what might cause memory spikes or memory corruption. What you will be learning today is to allow your application to free up space in case of a real emergency.

First, the basics.

Computer Memory

There are essentially two types of Computer memory: Volatile and Non-Volatile.

Volatile Memory

Volatile memory is computer memory that is reliant on power (electric or battery) to maintain the stored information. RAM, SRAM, and DRAM are the most common implementations of volatile memory.

Non-volatile Memory

Non-volatile memory is computer memory that is able to retain stored information irrespective of whether it is powered or not. Examples of non-volatile memory include DVDs, flash disks, and hard drives.

RAM (Random-access Memory)

Random-access memory allows data items to be read from or written to in almost the same amount of time irrespective of the location of data inside the memory.

Memory in Windows

Windows, as well as its applications and system processes, always references memory by the means of virtual memory addresses. These Virtual memory addresses get translated to RAM addresses by the hardware automatically.

Address Spaces

All processes that are running under on Windows are assigned virtual memory addresses. The Virtual address space ranges from 0 to 4,294,967,295 (4 GB), irrespective of how much RAM is physically installed on the computer. Windows occupies almost half of this virtual address space for the private use of each process; the other half is shared between all processes and the operating system.

Pagefiles

RAM is limited, whereas virtual memory is unlimited. When the memory being used by all the processes exceeds the available RAM on the PC, Windows moves pages of one or more virtual address spaces to the computer’s hard disk; thus freeing up that RAM frame for other uses.

Memory in .NET

.NET memory management frees the programmer from having to allocate and dispose of memory resources. Four sections of memory, also called heaps, are created to be used for storage upon the running of a .NET application. These four heaps are:

  • Code Heap: Code instructions get stored inside the Code Heap
  • Small Object Heap: Objects smaller than 85KB
  • Large Object Heap: Objects larger than 85KB
  • Process Heap: Default heap provided by the system

Stack

The above-mentioned Heaps get placed on a Stack. This area is known as a StackFrame; it contains all the Heaps of the certain app in one container. Other programs get placed in other StackFrames on the Stack.

.NET Garbage Collector

What does a garbage collector do? It collects garbage. What garbage depends on what you feed it and what you instruct it to collect. Making a call to GC.Collect() doesn’t collect the object(s) immediately; they just get placed inside a queue for disposal and, when needed, will get removed from memory.

Our Project

The project that you are going to create today is nothing fancy. It is an ordinary Windows Forms application containing one sole button. You can do this project in either C# or VB.NET. Once the form has loaded, add the following namespaces:

C#

using System.Runtime.InteropServices;

VB.NET

Imports System.Runtime.InteropServices

The System.Runtime.InterOpServices Namespace enables you to work with the Windows APIs. Add the APIs now:

C#

      [DllImport("KERNEL32.DLL", EntryPoint =
         "SetProcessWorkingSetSize", SetLastError = true,
         CallingConvention = CallingConvention.StdCall)]
      internal static extern bool SetProcessWorkingSetSize32Bit
         (IntPtr pProcess, int dwMinimumWorkingSetSize,
         int dwMaximumWorkingSetSize);

      [DllImport("KERNEL32.DLL", EntryPoint =
         "SetProcessWorkingSetSize", SetLastError = true,
         CallingConvention = CallingConvention.StdCall)]
      internal static extern bool SetProcessWorkingSetSize64Bit
         (IntPtr pProcess, long dwMinimumWorkingSetSize,
         long dwMaximumWorkingSetSize);

VB.NET

   <DllImport("KERNEL32.DLL", EntryPoint:= _
      "SetProcessWorkingSetSize", SetLastError:=True, _
      CallingConvention:=CallingConvention.StdCall)>
   Friend Shared Function SetProcessWorkingSetSize32Bit _
      (ByVal pProcess As IntPtr, ByVal dwMinimumWorkingSetSize _
      As Integer, ByVal dwMaximumWorkingSetSize As Integer) _
      As Boolean
   End Function

   <DllImport("KERNEL32.DLL", EntryPoint:= _
      "SetProcessWorkingSetSize", SetLastError:=True, _
      CallingConvention:=CallingConvention.StdCall)>
   Friend Shared Function SetProcessWorkingSetSize64Bit _
      (ByVal pProcess As IntPtr, ByVal dwMinimumWorkingSetSize _
      As Long, ByVal dwMaximumWorkingSetSize As Long) As Boolean

if you look closely at the preceding two APIs, you will notice that both make use of the same Windows function: SetProcessWorkingSetSize. One is declared for 32 bit Operating Systems, the other for 64 bit Operating Systems. You also will notice that the 32 bit implementation makes use of Integers, whereas the 64 Bit implementation makes use of Longs.

The SetProcessWorkingSetSize function sets the minimum and maximum working set sizes for the specified process.

Add the next Function to clear the memory:

C#

public void FlushMem()
      {
         GC.Collect();

         GC.WaitForPendingFinalizers();

         if (Environment.OSVersion.Platform == PlatformID.Win32NT)
         {

            SetProcessWorkingSetSize32Bit(System.Diagnostics
               .Process.GetCurrentProcess().Handle, -1, -1);

         }

         // if (Environment.Is64BitProcess)//
         //    Console.WriteLine("64-bit process");//
         // else//
         //    Console.WriteLine("32-bit process");//
      }

      private void button1_Click(object sender, EventArgs e)
      {

         FlushMem();

      }

VB.NET

   Private Sub FlushMem()

      GC.Collect()
      GC.WaitForPendingFinalizers()

      If Environment.OSVersion.Platform = PlatformID.Win32NT Then

         SetProcessWorkingSetSize32Bit(System.Diagnostics _
            .Process.GetCurrentProcess().Handle, -1, -1)

      End If

      'If Environment.Is64BitProcess Then'
      '   Console.WriteLine("64-bit process")'
      'Else'
      '   Console.WriteLine("32-bit process")'
      'End If'


   End Sub
   Private Sub Button1_Click(sender As Object, e As EventArgs) _
         Handles Button1.Click

      FlushMem()

   End Sub

This will flush your application’s memory. You will notice the comment. The comment makes use of Environment to determine if the Operating System is 64 bit or 32 bit.

Conclusion

It is possible to clean your RAM with your .NET programs, but be careful because this should not always be used. The best practice is still to let the Operating system and the Garbage Collector do their work.

Hannes DuPreez
Hannes DuPreez
Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read