Zach Rosenfield recently posted about an obscure memory leak issue when working with PowerShell and the SharePoint object model. You can read his post for details but in summary:
- PowerShell spawns a new thread for each pipeline (essentially any batch of code that runs together).
- SharePoint objects should not be used across multiple threads due to mechanics of the unmanaged heap.
- SharePoint objects used across multiple pipelines result in unmanaged heap leaks until PowerShell closes or you run out of memory.
This PowerShell behavior is easy enough to verify:
PS 1> function Get-ThreadId { [Threading.Thread]::CurrentThread.ManagedThreadId } PS 2> set-alias gt Get-ThreadId PS 3> gt 6 PS 4> gt 4
So we need to ensure that our SharePoint objects are allocated, used and disposed within the same thread. How can we do this? Zach offers two suggestions, but there are actually several options:
Single Line
PS 5> gt; gt; gt 10 10 10 PS 6> gt; ` >> gt; ` >> gt >> 3 3 3
Script
PS 7> @" >> gt >> gt >> "@ > gt.ps1 >> PS 8> .\gt.ps1 8 8
Function
PS 9> function gt2 { >> gt >> gt >> } >> PS 10> gt2 4 4
Inline Script Block
PS 11> &{ >> gt >> gt >> } >> 7 7
My Using Function
PS 12> using Microsoft.SharePoint PS 13> gt; using ($s = [Microsoft.SharePoint.SPSite] 'http://moss') { >> gt >> $s.Url >> gt >> } >> 5 5 http://moss 5
Cmdlet + Wrapper Class
Gary Lapointe has a set of PowerShell cmdlets that use wrapper objects to hide SPSite/SPWeb objects unless you specifically ask for them.
PS 14> $spi = Get-SPSite-gl 'http://moss' PS 15> gt; using ($s = $spi.SPBase) { $s.Url; gt } 8 http://moss 8
The severity of this issue depends on your environment and the kinds of scripts you’re running, but in memory-sensitive and production environments these are definitely some techniques to keep in mind.