【发布时间】:2015-06-03 10:55:00
【问题描述】:
ObjectPool 是 Roslyn C# 编译器中使用的一种类型,用于重用经常使用的对象,这些对象通常会被更新并经常被垃圾收集。这减少了必须发生的垃圾收集操作的数量和大小。
Roslyn 编译器似乎有几个独立的对象池,每个池都有不同的大小。我想知道为什么有这么多的实现,首选的实现是什么以及为什么他们选择 20、100 或 128 的池大小。
1 - SharedPools - 如果使用 BigDefault,则存储 20 个对象或 100 个对象的池。这个也很奇怪,因为它创建了一个新的 PooledObject 实例,当我们尝试池化对象而不是创建和销毁新对象时,这是没有意义的。
// Example 1 - In a using statement, so the object gets freed at the end.
using (PooledObject<Foo> pooledObject = SharedPools.Default<List<Foo>>().GetPooledObject())
{
// Do something with pooledObject.Object
}
// Example 2 - No using statement so you need to be sure no exceptions are not thrown.
List<Foo> list = SharedPools.Default<List<Foo>>().AllocateAndClear();
// Do something with list
SharedPools.Default<List<Foo>>().Free(list);
// Example 3 - I have also seen this variation of the above pattern, which ends up the same as Example 1, except Example 1 seems to create a new instance of the IDisposable [PooledObject<T>][3] object. This is probably the preferred option if you want fewer GC's.
List<Foo> list = SharedPools.Default<List<Foo>>().AllocateAndClear();
try
{
// Do something with list
}
finally
{
SharedPools.Default<List<Foo>>().Free(list);
}
2 - ListPool 和 StringBuilderPool - 不是严格分离的实现,而是围绕上面显示的专门针对 List 和 StringBuilder 的 SharedPools 实现的包装器。所以这会重用存储在 SharedPools 中的对象池。
// Example 1 - No using statement so you need to be sure no exceptions are thrown.
StringBuilder stringBuilder= StringBuilderPool.Allocate();
// Do something with stringBuilder
StringBuilderPool.Free(stringBuilder);
// Example 2 - Safer version of Example 1.
StringBuilder stringBuilder= StringBuilderPool.Allocate();
try
{
// Do something with stringBuilder
}
finally
{
StringBuilderPool.Free(stringBuilder);
}
3 - PooledDictionary 和 PooledHashSet - 它们直接使用 ObjectPool 并具有完全独立的对象池。存储一个包含 128 个对象的池。
// Example 1
PooledHashSet<Foo> hashSet = PooledHashSet<Foo>.GetInstance()
// Do something with hashSet.
hashSet.Free();
// Example 2 - Safer version of Example 1.
PooledHashSet<Foo> hashSet = PooledHashSet<Foo>.GetInstance()
try
{
// Do something with hashSet.
}
finally
{
hashSet.Free();
}
更新
.NET Core 中有新的对象池实现。请参阅我对C# Object Pooling Pattern implementation 问题的回答。
【问题讨论】:
-
考虑到微软一直反对 .NET 中对象池的概念,因为他们总是说 gen0 对象的 GC 非常快,这是一个有趣的转变 :-)
-
编译器不是一个您不希望暂停的实时应用程序......而且它不像 SO,数百个用户将同时连接。他们想要优化它,因为他们不希望它比旧编译器慢,并且他们使用了对象池......但这并不意味着我需要喜欢他们所做的。
-
我会做和他们一样的事情 :-) 而且我会同时感到聪明 和 肮脏 :-)
-
@xanatos 命令行编译器不关心暂停。 IDE 中内置的编译器可以。
-
我想这最终是关于性能的,所以我相信答案是基本的,而不是你想听到的。当你真正优化东西时,你并不真正关心那里有什么。你只是希望它尽可能快。所以,你找到一个热点,想一个可能的方法来优化它,看看现有代码是否完全符合你的要求 - 如果它不是完全你想要的,你只需实现它.在 HELL(高效低级代码)的世界中,没有“适当的设计”之类的东西;一切都是允许的,最终目标是唯一重要的事情。
标签: c# .net garbage-collection roslyn