Tuesday, November 11, 2014

How to dispose SPWeb and SPSite objects in PowerShell

Whenever we get the message below from ULS log, we need to monitor it carefully. If the highlighted number("16") keeps growing and rarely goes down, then it's pretty clear that there is some SPWeb or SPSite objects not been disposed properly.

Potentially excessive number of SPRequest objects (16) currently unreleased on thread 10. Ensure that this object or its parent (such as an SPWeb or SPSite) is being properly disposed. This object is holding on to a separate native heap.This object will not be automatically disposed.

Some objects, such as SPWeb.ParentWeb and SPSite.RootWeb don't need to be disposed manually. But in loop statement such as "foreach($site in $allSites)" or "foreach($web in $allWebs)", we have to call "Dispose()".

Another case is the objects created by "Get-SPSite" or "Get-SPWeb". I have to say it's quite easy (for me) to forget releasing them. Thanks for the post from Dave Wyatt, we get the "Using" feature just like what we have in C#.

Below is how I implemented it, and happy to share it here.

function UsingObject
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowEmptyCollection()]
        [AllowNull()]
        [Object]
        $InputObject,

        [Parameter(Mandatory = $true)]
        [scriptblock]
        $ScriptBlock
    )

    try
    {
        . $ScriptBlock
    }
    finally
    {
        if ($null -ne $InputObject -and $InputObject -is [System.IDisposable])
        {
            $InputObject.Dispose()
        }
    }
}

function DisposeSPSite([REF]$site)
{
    if ($site.Value -ne $null)
    {
        $site.Value.Dispose()
    }
}

function DisposeSPWeb([REF]$web)
{
    if ($web.Value -ne $null)
    {
        $web.Value.Dispose()
    }
}

UsingObject ($site = Get-SPSite -Identity $siteURL) {
$objSPWebApplication = $site.WebApplication
$siteCollections = $objSPWebApplication.Sites

foreach($itemSPSite in $siteCollections)
{
if ($itemSPSite.Url.IndexOf("Office_Viewing_Service_Cache") -ge 0)
{
DisposeSPSite ([REF]$itemSPSite)
continue
}

Blah, blah....

DisposeSPSite ([REF]$itemSPSite)
}
}

We still get the message below in ULS;

An SPRequest object was reclaimed by the garbage collector instead of being explicitly freed.  To avoid wasting system resources, dispose of this object or its parent (such as an SPSite or SPWeb) as soon as you are done using it.

It seems that after calling "Dispose()", the SPRequest resource is not released immediately. They are done by .Net garbage collection soon after.

One more question: Can we only use "Start-SPAssignment" and "Stop-SPAssignment" to release the resources? For small farm, maybe all right. But, it's definitely not recommended. With more and more resources occupied by one PowerShell session, the farm will slow down eventually, and it also generates huge ULS log files.


Any comments welcome!


References:

http://msdn.microsoft.com/en-us/library/aa973248(v=office.12).aspx#sharepointobjmodel__findingundisposedobjects

http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.administration.eventseverity(v=office.15).aspx

http://davewyatt.wordpress.com/2014/04/11/using-object-powershell-version-of-cs-using-statement/

1 comment:

  1. EXCELLENT information. Your directions are clear and concise, and easy to follow. Thanks for your hard work in posting this info.

    ReplyDelete