OdeToCode IC Logo

Look Ma, Almost No Dispose

Monday, May 23, 2005

The following code shows some interesting differences when executing under the 1.1 and 2.0 runtimes.

class Class1

{

    static void Main(string[] args)

    {

        try

        {

            for (Int32 i = 0; i < Int32.MaxValue; i++)

            {

                SqlConnection connection = new SqlConnection(connectionString);

                connection.Open();

                Thread.Sleep(30);

                Console.WriteLine(i.ToString());

            }

        }

        catch(Exception ex)

        {

            Console.WriteLine(ex.Message);

        }

 

        Console.WriteLine("Finished");

    }

 

    static string connectionString = "Data Source=.;Integrated Security=True";

}

The program eventuallys throw an exception under both versions of the runtime:: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use a nd max pool size was reached..

The difference is, under 1.1 the program makes roughly 140 iterations, while under 2.0 the program makes roughly 900+ iterations. In both cases max pool size defaults to 100 connections. Perfmon shows the follwing, where the yellow line is the SQL Server statistic for # of actual connections, and the purple line tracks gen 0 garbage collections. When the yellow line hits 100 - program go boom.

1.1 2.0

Under 1.1 the garbage collector finishes 3 gen 0 collections before the runtime throws an exception, under 2.0 the collector finishes 16 gen 0 collections. I wouldn’t think these collections are triggered by memory pressure, so I'm wondering what other logic may lay beneath the connection pooling mechanism in 2.0. It’s hard to see what is happening under the hood because Reflector is misbehaving on my beta 2 install. Anyone have ideas?