Addressing the PowerShell Garbage Collection bug

by Jeremy Saunders on September 25, 2017

There is a known bug where PowerShell does not correctly manage a garbage collection whilst executing a pipeline or loop of an object.

Simply using [System.GC]::Collect() within the pipeline or loop does not work as expected. Memory continually grows until the pipeline or loop has completed. This becomes a serious problem if you’re script is processing large objects. You can potentially exhaust memory resources and your script will fail with out of memory errors. This has been driving me nuts for years, as many of my Active Directory Health Check, Audit and Remediation Scripts process large objects in large environments.

There is a good overview of the bug here: No garbage collection while PowerShell pipeline is executing. Whilst this post claims that it seems to have been resolved in PowerShell 5, this doesn’t appear to be the case from my testing.

Here’s what was happening to powershell.exe after processing a couple of hundred users using my script to create a Kerberos Token Size Report

PowerShell Garbage Collection bug

It continually grows…and grows…and grows.

Just by chance, and trying to add some smarts to my scripts for memory usage reporting, I found a way around this. Simply use…

[System.GC]::GetTotalMemory(‘forcefullcollection’) | out-null

or

[System.GC]::GetTotalMemory($true) | out-null

Where $true or ‘forceFullCollection’ is used to indicate that this method can wait for garbage collection to occur before returning.

Even though [System.GC]::Collect() does not force a garbage collection whilst executing in a pipeline or loop of an object, [System.GC]::GetTotalMemory() with either $true or ‘forceFullCollection’ does indeed successfully force a garbage collection. Wow! Go figure! My testing so far has found this to be a reliable method to use across different versions of PowerShell.

So now here is what happens to powershell.exe after processing a couple of hundred users using the same script…

PowerShell Garbage Collection bug gone after fix

A massive difference in memory usage, which doesn’t really grow too much further even after processing tens of thousands of users in the object.

I’ve actually taken this one step further and have started to add a function to my scripts to dynamically report on memory allocated to the managed components. It only reports the bytes allocated by the PowerShell scripting engine and does not account for the memory used by the PowerShell engine itself as can be seen in Task Manager. But I really like this function:

This is one of the many vagaries of PowerShell that’s been annoying me for a very long time.

I hope this helps anyone else struggling with the same challenges.

Happy scripting 🙂

Jeremy Saunders

Jeremy Saunders

Technical Architect | DevOps Evangelist | Software Developer | Microsoft, NVIDIA, Citrix and Desktop Virtualisation (VDI) Specialist/Expert | Rapper | Improvisor | Comedian | Property Investor | Kayaking enthusiast at J House Consulting
Jeremy Saunders is the Problem Terminator. He is a highly respected IT Professional with over 35 years’ experience in the industry. Using his exceptional design and problem solving skills with precise methodologies applied at both technical and business levels he is always focused on achieving the best business outcomes. He worked as an independent consultant until September 2017, when he took up a full time role at BHP, one of the largest and most innovative global mining companies. With a diverse skill set, high ethical standards, and attention to detail, coupled with a friendly nature and great sense of humour, Jeremy aligns to industry and vendor best practices, which puts him amongst the leaders of his field. He is intensely passionate about solving technology problems for his organisation, their customers and the tech community, to improve the user experience, reliability and operational support. Views and IP shared on this site belong to Jeremy.
Jeremy Saunders
Jeremy Saunders

Previous post:

Next post: