The example code below allocates a large number of sync blocks. This is displayed in Performance Monitor as "# of sink blocks in use". However after releasing the locks, the counter will not decrease until I manually call GC.Collect(). Why doesn't this happen automatic or isn't this a problem?
using System;
using System.Collections.Generic;
using System.Threading;
namespace locktest
{
    public class Program
    {
        static void Main(string[] args)
        {
            var list1 = new List<Object>();
            int threadcount = 0;
            const int maxthreads = 9000;
            for (int i = 0; i < maxthreads; i++)
            {
                var obj = new Object();
                list1.Add(obj);
                Monitor.Enter(obj); // this will initially use just a thin lock
                // lock from a different thread to move to the sync blocks table
                ThreadPool.QueueUserWorkItem(o =>
                {
                    Monitor.TryEnter(o, 1);
                    Interlocked.Increment(ref threadcount);
                }, obj);
            }
            while (threadcount < maxthreads)
                Thread.Sleep(100); // wait until all threads have been finished
            foreach (var obj in list1)
                Monitor.Exit(obj); // release the locks
            list1.Clear();
            Console.WriteLine("Press enter to force GC.");
            Console.ReadKey();
            GC.Collect(); // this will reduce the number of sync blocks to almost 0
            Console.WriteLine("Press enter to quit.");
            Console.ReadKey();
        }
    }
}
related: What is a “Sync Block” and tips for reducing the count
