views:

387

answers:

3

We have a server component written in .Net 3.5. It runs as service on a Windows Server 2008 Standard Edition. It works great but after some time (days) we notice massive slowdowns and an increased working set. We expected some kind of memory leak and used WinDBG/SOS to analyze dumps of the process. Unfortunately the GC Heap doesn’t show any leak but we noticed that the JIT code heap has grown from 8MB after the start to more than 1GB after a few days.

We don’t use any dynamic code generation techniques by our own. We use Linq2SQL which is known for dynamic code generation but we don’t know if it can cause such a problem.

The main question is if there is any technique to analyze the dump and check where all this Host Code Heap blocks that are shown in the WinDBG dumps come from?

[Update]

In the mean time we did some more analysis and had Linq2SQL as probable suspect, especially since we do not use precompiled queries. The following example program creates exactly the same behaviour where more and more Host Code Heap blocks are created over time.

using System;
using System.Linq;
using System.Threading;

namespace LinqStressTest
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 100; ++ i)
                ThreadPool.QueueUserWorkItem(Worker);
            while(runs < 1000000)            
            {
                Thread.Sleep(5000);
            }
        }

        static void Worker(object state)
        {
            for (int i = 0; i < 50; ++i)
            {
                using (var ctx = new DataClasses1DataContext())
                {
                    long id = rnd.Next();
                    var x = ctx.AccountNucleusInfos.Where(an => an.Account.SimPlayers.First().Id == id).SingleOrDefault();
                }
            }
            var localruns = Interlocked.Add(ref runs, 1);
            System.Console.WriteLine("Action: " + localruns);
            ThreadPool.QueueUserWorkItem(Worker);
        }

        static Random rnd = new Random();
        static long runs = 0;
    }
}

When we replace the Linq query with a precompiled one, the problem seems to disappear.

A: 

In some scenario's, .NET does dynamic code generation for you. Two examples I can think of are regular expressions and xml serialization. Search your code base for this. Perhaps others know other parts of the .NET framework that do dynamic code generation.

Steven
Using the above example program we were able to show that Linq2SQL can trigger this behaviour.
In this scenario there is no dynamic code generation
Vladimir Lifliand
A: 

The only way i know of memory leakage in .net is due to events handling, check this out:

  1. link1
  2. also, take a look at the following question:How do I avoid a memory leak with LINQ-To-SQL?
  3. also, take a look into ANTS profiler
  4. And, have you considered, that maybe, at some point you will have many many threads running? which will nativly consume lots of memory?
MindFold
Thanks for the tips. Using WinDBG we were able to show that we not leaking memory in the managed heap, also we always through away our DataContext after the unit of work is done.
+1  A: 

Use a 'singleton' DataContext, instead of recreating it all the time in the loop.

I'm sure the effect will be the same as compiled queries.

Update:

This issue is meant to be 'rectified' in .NET 4 as it supports GC'able dynamic assemblies.

leppie