views:

50

answers:

2

What is the proper way of disposing objects created inside a function? I came across this method on a website.

function get-spweb ([String]$webUrl=$(throw 'Parameter -webUrl is missing!'))
{
   $site = get-SPSite $weburl
   return $site.OpenWeb()
   $site.Dispose()
}

Does the Dispose method ever get called in this function?

+2  A: 

No it doesn't because you return out of the function first before calling Dispose. If you have resources that have to be disposed then I would use a try/finally statement like so:

$site = Get-SPSite $weburl
try {
 # do stuff to $site until done with it
}
finally {
    $site.Dispose()
}

The nice thing about finally is that the dispose will get called no matter how you exit the try block (either successfully, because of an error or because of a return statement).

Keith Hill
A: 

First up, you don't actually want Dispose called here anyway - when you call Dispose on an SPSite instance, all webs returned via its OpenWeb are also disposed because they are "owned" by that SPSite!

One of the models used by SharePoint 2010's cmdlets is a kind of "deferred disposal" which means that SPWeb instances are not disposed until the pipeline in which they are involved in completes. This works like this:

function Get-SPWeb {
     param([uri]$Url)

     begin {
         # get SPSite that owns the passed Url
         $site = new-object microsoft.sharepoint.spsite $url
         # return specific SPWeb instance
         $site.OpenWeb() 
     }
     end {
         # this disposes owning spsite AND the returned web
         $site.Dispose()
     }
}

Now here is how this works in practice (this is a single line):

ps> get-spweb "http://localhost/sites/test" | foreach-object {
    $_.Title = "New Name"; $_.update()
}

The first portion will obtain a single SPWeb instance and pass it to the ForEach-Object portion. Only when the foreach completes (and finishes changing the web's title) will the corresponding End block be called in get-spweb, which disposes the site and web. What's important is that entire pipeline is a single block of code that is executed in a single call.

This won't work interactively like this:

ps> $w = get-spweb "http://localhost/sites/test" # calls begin AND end
ps> $w.title = "new name"
ps> $w.update() # boom! web is already disposed

So in this latter example you'd have to use a different implementation of get-spweb (one that omits the end block, or suppresses it with a switch parameter) and then you'd have to dispose the site yourself.

Another important detail is that working interactively in powershell with sharepoint objects will cause you to leak memory. By default, powershell runs in MTA (multi-threaded apartment) and will use a pool of threads to execute your commands. Each line entered will use a different thread. Each time you access a COM object with a different thread, you will leak some memory from the unmanaged heap as a new heap is allocated for the context switch (without the old one being freed.) This can be alleviated by starting powershell.exe with the -STA switch. This will ensure that all commands and pipelines are executed with the same thread, avoiding the memory leak. That said, simply closing the powershell console will regain all of that memory again but long running scripts might starve your servers of memory if you're not careful, bringing down SharePoint (anything else that doesn't like to get starved of working set.) This is why the single-line approach works so well in the former example: the object is allocated and disposed in the same pipeline, and by extension, the same thread. No leak.

x0n