Friday, April 25, 2014

Microsoft.Web.Administration Memory Leak

*DISCLAIMER: THIS IS REPORTED FIXED IN IIS 8.0*

I work with the ServerManager class from the Microsoft.Web.Administration assembly quite a bit on a project for work. We use it to track websites performance, etc… After looking in dotPeek I found out that this assembly is just a COM wrapper for IIS management. As I started deploying this software to servers with more websites, I noticed that the memory growth for my monitoring process was going through the roof (usually runs with 90-150mb memory, I had instances over 9gb of memory).

After going through the paces of doing memory dumps, using windbg to check the memory, there was a very large native heap, while the managed heap was in the expected range. After some google-fu, I found this bug reported on the Visual Studio feedback site.

The following code which uses Microsoft.Web.Administration.dll, version 7, causes a memory leak. No way to work around this issue without running it in a separate process.
namespace testmemoryleak
{
    using System;
    using System.Threading;
    using Microsoft.Web.Administration;
    static class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                using (var mgr = new ServerManager())
                {
                    Console.WriteLine(mgr.WorkerProcesses.Count);
                }
                Thread.Sleep(1);
            }
        }
    }
}

That was my exact problem, and this code duplicated it 100%. I then opened up a support ticket with Microsoft, and quickly they gave me a work around for the leak (posted to the thread mentioned above as well).

Instead of:

namespace testmemoryleak
{
using System;
using System.Threading;
using Microsoft.Web.Administration;
static class Program
{
static void Main(string[] args)
{
while (true)
{
using (var mgr = new ServerManager())
{
Console.WriteLine(mgr.WorkerProcesses.Count);
}
Thread.Sleep(1);
}
}
}
}

I use the static method on the ServerManager OpenRemote("localhost")

namespace testmemoryleak
{
using System;
using System.Threading;
using Microsoft.Web.Administration;
static class Program
{
static void Main(string[] args)
{
while (true)
{
using (var mgr = ServerManager.OpenRemote("localhost"))
{
Console.WriteLine(mgr.WorkerProcesses.Count);
}
Thread.Sleep(1);
}
}
}
}

Once deployed, the memory leak was gone! Oddly enough, the IIS Management Snap-In (inetsrv) also uses the remote connectivity as well, so it would have never suffered from it.


I hope this post helps other people out there who suffer from the same leak.

2 comments:

Phil Dobson said...

Thank you Tom you saved my bacon

I had a similar issue with a memory leak. In my case I was making a series of calls in order to delete a site (and its application pool and its config location node). I load tested this and after deleting 30 sites I would get System.Runtime.InteropServices.COMException (0x80070008): Not enough storage is available to process this command. (Exception from HRESULT: 0x80070008) when calling CommitChanges() observing that my service had used up about 1.8 GB of RAM. Your work-around solved my issue completely. I am using IIS 8.5 on Windows Server 2012 R2 and version 8.5.9600.17042 of the Microsoft.Web.Administration.dll

Ember said...

I found that memory leak in such codes:
public bool ExistAppPool(string appPoolName)
{
return _serverManager.ApplicationPools[appPoolName]
}

and
Site oneSite = _serverManager.Sites[siteName];

Such [ ] operation on Collection cause 3-6MB memory leak.
But the way you find out to across the problem is also applicable for this.
Thanks for your resolution!!The memory leak is gone!