I recently had to deal with a pretty bad memory leak on a project. Over the course of a few hours, an ASP.NET project was slowly increasing in memory from a few mb to close to two gigs before finally crashing from OutOfMemoryExceptions.
I did a lot of googling, read up on the large object heap, checked that things were properly being disposed, fixed a few other bugs that were popping up as a side effect to the main issue. None of it was making a dent in the memory creep, though!
Then I installed ANTS Memory Profiler and did my damnedest to hammer in order to find what was up. First thing I noticed was that there was a good 600mb of strings that were just sitting around. A closer look showed that the largest strings were many many duplicate copies of generated sql statements from NHibernate. Looking at what was keeping the strings around I was able to find that they were being cached inside an instance of SessionFactoryImpl.
That seemed okay, the project uses a singleton for the ISessionFactory instance. It's supposed to be a singleton, anyway. I noticed that there was more than once instance floating around, each one holding a duplicate copy of all those sql strings.
It turned out that a recent change to the project pointed out a flaw in how StructureMap was being initialized. In all of our other projects, StructureMap is initialized during the Application_Start event. For this project, however, we were initializing StructureMap from the HttpApplication.Init method. What's the difference? The lifetime of an ASP.NET application results in many instances of your HttpApplication being created. The Init method is called on every instance. The Application_Start event, though, is only called one time when the application actually starts up(as the name suggests!).
So what was the problem, exactly? The application kept reregistering the ISessionFactory singleton, overwriting the previous instance with a new one every time a new HttpApplication instance was created. This wouldn't normally be a problem, except NHibernate keeps its own cache of all existing ISessionFactory instances, and StructureMap does not dispose instances as they're replaced. Over time, NHibernate ended up with hundreds of ISessionFactory instances in its cache that it didn't know it could get rid of.