SEO Expert Jared Nielsen Troubleshoots Memory Leak Errors in BlogEngine.net 1.6.1 and IIS 7

December 15, 2010 at 7:02 PMJaredNielsen

Fixing Memory Leaks in BlogEngine.net version 1.6.1

 We love warping Blogengine.net into applications that most people would never dream of.  This applies to even some of our larger clients.  In the process of pushing Blogengine.net to its limits, we began to see some if the limits of BlogEngine.  We also gained a larger appreciation of the open source community since there are some really talented software developers out there that comb through these open source blogging platforms and learn it inside and out.  My hat has to go out to Peter Kuhn aka Mister GoodCat of Pitorque Software (www.Pitorque.de).  He helped us (through his blog post about memory leaks in BlogEngine.net) resolve a critical problem with one our sites that used BlogEngine.net.  Hats off to Peter!

Using Task Manager to See Memory Leaks

I first saw the problem when I was cruising my task manager (a wonderful hobby of mine) and discovered a whopping memory allocation exceeding 1GB.  Now we have some large sites, but this clearly was inching up every time a page was viewed and needed to be dealt with.

As helpful as Task Manager is, knowing that a worker process is out of control is nice but WHICH ONE?   Since we use host headers to map many websites to the same IP address, and we had multiple IIS configurations working on the same server, I saw the runaway application but didn't know how to identify which site wwas causing the issue.

Identifying IIS Worker Processes with the PID number

First I needed the specific PID number for the worker process (which is hidden by default).  By using the "View | Select Columns" menu in Task Manager I was able to check the box that made the PID of the worker application appear:

Now we know that our runaway application is number 3584.  Great.  As always, I learn by precept and precept so I continued to dig, finding an article online that allowed me to execute a special program that lists the IIS application name of any give worker process (by PID or "Process Identifier").

Running AppCMD to Identify IIS Applications Mapped to a Worker Process by PID

Now we need to discover which application is causing the problem.  Here we identify a highly trafficked website that is causing the issue. 

This is a website that has a large amount of traffic, benefitting from a large radio advertising budget and pay per click spend.  It appears also that the memory climbs per page view so it will just climb and climb and climb.

Repairing BlogEngine.net 1.6.1

Here is where our hero Peter Kuhn comes in.  He was able to identify that the open source BlogEngine.net version 1.6.1 platform was not using static constructors properly within the code.

Blogroll Static Constructor

The first issue was the BlogRoll.  He identified that "the BlogRollItem.Saved event is hooked up every time the control is created (and never released). To change that, make the constructor static, as well as the BlogRollItem_Saved event handler." (Peter Kuhn, www.Pitorque.de).  His fix was to simply make the constructor static and recompile the code:

static Blogroll()
{
    BlogRollItem.Saved += new EventHandler<SavedEventArgs>(BlogRollItem_Saved);
}


private static void BlogRollItem_Saved(object sender, SavedEventArgs e)
{
    ..

Code Formatter Static Constructor

There is a similar problem in many places within the CodeFormatter class.  The following elements needed to be set to Static Constructors:

  • The "Regex codeRegex" field
  • The "ServingContent" event handler method
  • The "CodeEvaluator" method
  • The "Highlight" method

Widget Zone Memory Leak Issues

Then his open source compatriots delved into a lengthy fix-find-fix-find iteration where they began to dig into the more esoteric implementations of WidgetZones (the box containing replaceable and editable widgets ... normally on the left sidebar).  The eventual solution was to "Make the constructor static and change the event handler for the saved event, add a field for the dictionary and change the previous field XML_DOCUMENT to be a property that fetches the document from the dictionary." (Peter Kuhn, www.Pitorque.de)

static WidgetZone()
{            
    WidgetEditBase.Saved += delegate { ReloadAllXml(); };
}


private static Dictionary<stringXmlDocument> _xmlDocumentByZone = new Dictionary<stringXmlDocument>();


private const string DefaultZoneName = "be_WIDGET_ZONE";


// For backwards compatibility or if a ZoneName is omitted, provide a default ZoneName.
private string _ZoneName = DefaultZoneName;
                
private XmlDocument XML_DOCUMENT
{
    get
    {
        // look up the document by zone name
        if (_xmlDocumentByZone.ContainsKey(ZoneName))
        {
            return _xmlDocumentByZone[ZoneName];
        }


        return null;
    }
}

"The "RetrieveXml" method now is static and takes a zone name as parameter. The "ReloadAllXml" method is new." (Peter Kuhn, www.Pitorque.de)

private static void ReloadAllXml()
{
    // simply reload all xml documents when something has changed
    Dictionary<stringXmlDocument> newDocs = new Dictionary<stringXmlDocument>();


    foreach (string zoneName in _xmlDocumentByZone.Keys)
    {
        XmlDocument doc = RetrieveXml(zoneName);
        newDocs.Add(zoneName, doc);
    }


    _xmlDocumentByZone = newDocs;
}


private static XmlDocument RetrieveXml(string zoneName)
{            
    WidgetSettings ws = new WidgetSettings(zoneName);
    ws.SettingsBehavior = new XMLDocumentBehavior();
    XmlDocument doc = (XmlDocument)ws.GetSettings();
    return doc;
}

"During initialization, the document for a zone is added to the caching dictionary if it hasn't been loaded yet": (Peter Kuhn, www.Pitorque.de)

protected override void OnInit(EventArgs e)
{
    if (XML_DOCUMENT == null)
    {
        // if there's no document for this zone name yet, load it
        XmlDocument doc = RetrieveXml(ZoneName);


        if (_xmlDocumentByZone.ContainsKey(ZoneName))
        {
            _xmlDocumentByZone[ZoneName] = doc;
        }
        else
        {
            _xmlDocumentByZone.Add(ZoneName, doc);
        }
    }


    base.OnInit(e);
}

Great News!

What was the outcome?  A stunning decrease in load on our server from this single application of over 95%!  I guess I can host a few more websites on this server.  Thanks Mr. Kuhn!

For more information on this fix, get it from the source!  Visit Peter Kuhn's personal blog at http://www.pitorque.de/MisterGoodcat/post/Fixing-the-memory-leaks-in-BlogEngineNET-1-6-1.aspx and keep tabs on any updates to this fix including a download of the source code file.

Jared Nielsen
[email protected]
The FUZION Agency
www.FUZION.org

 

 

 

Pingbacks and trackbacks (1)+

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

Tag cloud