现在的服务器都是多个cpu,在.NET Framework 2.0在GC上有个新特性GCServer ,不知道有多少人用过这个东东。
关于GC可以看这篇文章GC是如何工作的,不同的代的大小是多少,文章中提到有三种模式的GC,分别对应优化不同类型的应用程序。
Server GC
这种类型的GC是针对服务器端高吞吐量和高扩展性进行优化的,那情况是一种长时间的加载和请求不停地分配和重新分配,并维持在较高水准的情况。
这种server GC 使用每个处理器一个堆、一个GC线程,并尽量的保持堆之间的平衡。在垃圾收集的时候,GC线程工作在各自的线程中,这样就最小化了锁资源,就保证了在这种应用条件下最有效的工作。
这种类型的GC只有在多处理器的机器上可见,如果你在单处理器上的设置这种模式,那你将得到实际运行的模式是非并发的workstation版本(Non Concurrent)。现在的双核也是这种模式,intel的超线程技术实现的cpu并不是真实的多cpu,因此它不会使用这种模式。
Asp.net 在多cpu的机器上默认使用这种模式,如果你想使用server GC模式,你可以在应用程序级别上做如下设置:
<configuration>
<runtime>
<gcServer enabled="true" />
</runtime>
</configuration>
Workstation GC – Concurrent
这种被用来作为winform应用程序和windows services 服务程序的默认设置。
这种模式是对交互的应用程序,这种程序要求应用程序不能暂停,即时一个相对很短暂的时间也是不行的。因为暂停进程会让用户界面闪烁或者当点击按钮的时候感觉应用程序没有响应。
这种实现方式是当进行Gen 2 收集的时候,将cpu和内存的使用量作为更短的停顿时间。
Workstation GC – Non Concurrent
这种模式是模仿Server GC,只是收集是发生在引起GC的进程上,这种模式推荐为那种运行在单个cpu上的服务类型的应用程序。可以修改应用程序级上的配置来把 concurrency 关闭。
<configuration>
<runtime>
<gcConcurrent enabled="false" />
</runtime>
</configuration>
启用GCServer有什么意义呢,主要是会进行垃圾整理,这样就可以避免垃圾脆片的问题影响。需要验证这个问题的例子可以参见这篇文章Memory Management (III) - .NET CLR ?,文章中包含一个测试代码。代码如下:
class Program
{
static void Main(string[] args)
{
List<byte[]> buffer1 = new List<byte[]>();
List<byte[]> buffer2 = new List<byte[]>();
List<byte[]> buffer3 = new List<byte[]>();
// // allocate //
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("1. Allocate 64mb block(s) as more as possible...");
try
{
while (true)
{
buffer1.Add(new byte[64 * 1024 * 1024]);
Console.Write("#");
buffer2.Add(new byte[64 * 1024 * 1024]);
Console.Write("#");
}
}
catch (OutOfMemoryException)
{
}
Console.WriteLine();
Console.WriteLine(" Total {0} blocks were allocated ( {1} MB).", (buffer1.Count + buffer2.Count), (buffer1.Count + buffer2.Count) * 64);
// // free //
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("2. Free Blocks...");
buffer2.Clear();
Console.WriteLine(" Total: {0} blocks ({1} MB)", buffer1.Count, buffer1.Count * 64);
// // GC //
GC.Collect(GC.MaxGeneration);
// // allocate //
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("3. Allocate 72mb block(s) as more as possible...");
try
{
while (true)
{
buffer3.Add(new byte[72 * 1024 * 1024]);
Console.Write("#");
}
}
catch (OutOfMemoryException)
{
}
Console.WriteLine();
Console.WriteLine(" Total: 64mb x {0}, 72mb x {1} blocks allocated( {2} MB).\n", buffer1.Count, buffer3.Count, buffer1.Count * 64 + buffer3.Count * 72);
Console.ReadLine();
}
}