Freeing up memory in PowerShell using garbage collector

Just got this great tip from Lars on how he reduces memory consumption in his AD PowerShell scripts with a simple garbage collection call (basically explicitly telling .NET behind PowerShell to recycle the objects no longer in use):

Using Quest AD tools I often run in to memory consumption problems. I thought it was a question of memoryleaks, but its not, its the Garbage collection that doesn’t get collection until its to late.
So i’m using this when I use Quests AD Management Cmdlets in PowerShell, where $i is a simple counter

Explicitly calling Garbge collector is not a good idea generally. The garbace collector is optimized to run at specific intervals depending on memory usage. Manually calling the GC disturbs the calculation,

Indeed one should use caution: in your particular case resulting performance may get better or worse. In this particular situation though, one of the users reported that he is getting much lower memory consumption when in between his AD management with PowerShell inside PowerGUI Script Editor he is using garbage collection calls – so I wanted to pass the knowledge.

There are good reasons to run the garbage collector explicitly on an occasional basis. The GC runs on a separate thread and will only operate on objects of a .NET process when the process thread yields. If a process does not yield then the GC will not run and the process can crash with an out of memory exception.

This can happen (does happen to my processes) when I run long running tasks via a scheduled task. It took me a while to realise why a process run in the PoSh GUI always worked but would fail when run as a scheduled task.

Hindsight being a wonderful thing, its now obvious that when a process is run in a GUI the process itself is run on a background thread and the GUI thread will periodically yield (because that’s what GUIs do) giving the GC chance to work.

When the same script is run as a scheduled task there is nothing to cause the scheduled task thread to yield so the GC never has an opportunity to work.

The solution is to call [System.GC]::Collect() and then the sleep cmdlet for a few milliseconds. Calling sleep is important because this causes the process thread to yield giving the GC the opportunity it needs to work,

Legal

The posts on this blog are provided “as is” with no warranties and confer no rights. The opinions expressed on this site are mine and mine alone, and do not necessarily represent those of my employer - WSO2 or anyone else for that matter. All trademarks acknowledged.