高性能托管数组缓冲池,可重复使用,用租用空间的方式代替重新分配数组空间的行为
可以在频繁创建和销毁数组的情况下提高性能,减少垃圾回收器的压力
var pool=ArrayPool[byte].Shared
byte[] array=pool.Rent(1024)
pool.Return(array)
Shared返回为一个静态共享实例,实际返回了一个TlsOverPerCoreLockedStacksArrayPool
internal sealed class TlsOverPerCoreLockedStacksArrayPool<T> : ArrayPool<T>
{
private static readonly TlsOverPerCoreLockedStacksArrayPool<T> s_shared = new TlsOverPerCoreLockedStacksArrayPool<T>();
public static ArrayPool<T> Shared => s_shared;
}
private static void TimeMonitor()
{
//随机生成3000个数组的长度值
var sizes = new int[30000];
Parallel.For(0, 10000, x => { sizes[x] = new Random().Next(1024 * 800, 1024 * 1024); });
//缓冲池方式租用数组
var gcAllocate0 = GC.GetTotalAllocatedBytes();
var watch = new Stopwatch();
Console.WriteLine("start");
watch.Start();
for (int i = 0; i < 10000; i++)
{
//CreateArrayByPool(ArrayPool<int>.Shared, 1024 * 1024,sizes[i], false);
var arr = ArrayPool<int>.Shared.Rent(sizes[i]);
for (int j = 0; j < sizes[i]; j++)
{
arr[j] = i;
}
ArrayPool<int>.Shared.Return(arr, true);
}
var time1 = watch.ElapsedMilliseconds;
var gcAllocate1 = GC.GetTotalAllocatedBytes(true);
//new 方式分配数组空间
watch.Restart();
for (int i = 0; i < 30000; i++)
{
//CreateArrayDefault(i, sizes[i], false);
var arr = new int[sizes[i]];
for (int j = 0; j < sizes[i]; j++)
{
arr[j] = i;
}
}
var time2 = watch.ElapsedMilliseconds;
var gcAllocate2 = GC.GetTotalAllocatedBytes(true);
Console.WriteLine("ArrayPool方式创建数组耗时:" + time1 + " Gc总分配量" + (gcAllocate1 - gcAllocate0));
Console.WriteLine("默认方式创建数组耗时:" + time2 + " Gc总分配量" + (gcAllocate2 - gcAllocate1 - gcAllocate0));
}
内存使用截图:左侧没有波动的横线是缓冲池执行的过程,右侧为手动创建数组的执行过程
执行结果:
ArrayPool方式创建数组耗时:17545 Gc总分配量4130800
默认方式创建数组耗时:26870 Gc总分配量37354100896
private static void PostFileByBytesPool(FormFile file)
{
HttpClient client = new HttpClient() { BaseAddress = new Uri("https://fileserver.com") };
var fileLen = (int)file.Length;
var fileArr = ArrayPool<byte>.Shared.Rent(fileLen);
using var stream = file.OpenReadStream();
stream.Read(fileArr, 0, fileLen);
MultipartFormDataContent content = new MultipartFormDataContent();
content.Add(new ByteArrayContent(fileArr, 0, fileLen), "id_" + Guid.NewGuid().ToString(), file.FileName);
client.PostAsync("/myfile/" + file.FileName, content).Wait();
ArrayPool<byte>.Shared.Return(fileArr, true);
}
ArrayPool的Create()函数会创建一个ConfigurableArrayPool对象
ConfigurableArrayPool的构造函数接收两个参数
1024*1024*1024
通过这两个参数可以解决Shared方式的两个问题:
示例
//创建一个自定义缓冲池实例,单个数组最大长度为1024 * 2048,最大可同时租用10个缓冲区
ArrayPool<int> CustomerArrayPool = ArrayPool<int>.Create(1024 * 2048,10);
与Shared不同的是,如果设置CustomerArrayPool=Null
那么在下一次垃圾回收时该缓冲池所占的内存会立马全部释放。
为防止不可预测的风险,应该保持CustomerArrayPool的存活。
同时为了防止内存的滥用应该限制CustomerArrayPool的数量