.Net性能調(diào)優(yōu)-ArrayPool詳情
1、使用
- 獲取緩沖池實(shí)例 :
Create / Shared var pool=ArrayPool[byte].Shared - 調(diào)用緩沖池實(shí)例 :
Rent()函數(shù),租用緩沖區(qū)空間byte[] array=pool.Rent(1024) - 調(diào)用緩沖池實(shí)例 :
Return(array[T])函數(shù),歸還租用的空間pool.Return(array)
2、Shared
Shared返回為一個(gè)靜態(tài)共享實(shí)例,實(shí)際返回了一個(gè) 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;
}
2.1特點(diǎn)
租用數(shù)組長(zhǎng)度不可超過(guò) 2^20( 1024*1024 = 1 048 576),否則會(huì)從GC中重新開(kāi)辟內(nèi)存空間
Rent 租用數(shù)組實(shí)際返回的長(zhǎng)度可能比請(qǐng)求的長(zhǎng)度大,返回長(zhǎng)度一是(16*2^n)歸還緩沖區(qū)的時(shí)候,如果不設(shè)置
Return clearArray ,下一個(gè)租用者可能會(huì)看到之前的填充的值(在返回的數(shù)組長(zhǎng)度剛好是下一個(gè)租用者請(qǐng)求的長(zhǎng)度時(shí)會(huì)被看到)
緩沖池的內(nèi)存釋放不是實(shí)時(shí)釋放,在緩沖區(qū)空閑時(shí),大概10到20秒之后,會(huì)隨著第2代GC一起釋放,分批釋放
并發(fā)數(shù)量持續(xù)增長(zhǎng)時(shí),緩沖池占用的內(nèi)存空間也會(huì)持續(xù)增長(zhǎng),而且似乎沒(méi)有上限
2.2耗時(shí)對(duì)比
private static void TimeMonitor()
{
//隨機(jī)生成3000個(gè)數(shù)組的長(zhǎng)度值
var sizes = new int[30000];
Parallel.For(0, 10000, x => { sizes[x] = new Random().Next(1024 * 800, 1024 * 1024); });
//緩沖池方式租用數(shù)組
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 方式分配數(shù)組空間
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方式創(chuàng)建數(shù)組耗時(shí):" + time1 + " Gc總分配量" + (gcAllocate1 - gcAllocate0));
Console.WriteLine("默認(rèn)方式創(chuàng)建數(shù)組耗時(shí):" + time2 + " Gc總分配量" + (gcAllocate2 - gcAllocate1 - gcAllocate0));
}
內(nèi)存使用截圖:左側(cè)沒(méi)有波動(dòng)的橫線是緩沖池執(zhí)行的過(guò)程,右側(cè)為手動(dòng)創(chuàng)建數(shù)組的執(zhí)行過(guò)程

執(zhí)行結(jié)果:
ArrayPool方式創(chuàng)建數(shù)組耗時(shí):17545 Gc總分配量4130800
默認(rèn)方式創(chuàng)建數(shù)組耗時(shí):26870 Gc總分配量37354100896
2.3示例(前端文件通過(guò)后端Api上傳OSS)
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);
}
3、Create()
ArrayPool 的Create()函數(shù)會(huì)創(chuàng)建一個(gè) ConfigurableArrayPool 對(duì)象
ConfigurableArrayPool 的構(gòu)造函數(shù)接收兩個(gè)參數(shù)
- maxArrayLength :單次租借的數(shù)組最大長(zhǎng)度,不可超過(guò)
1024*1024*1024 - maxArraysPerBucket :最多可以存在的未歸還緩沖區(qū)數(shù)量
通過(guò)這兩個(gè)參數(shù)可以解決 Shared 方式的兩個(gè)問(wèn)題:
- 自定義單個(gè)數(shù)組的最大長(zhǎng)度,可以獲取更大的內(nèi)存空間用來(lái)存儲(chǔ)大文件等
- 限定了數(shù)組的長(zhǎng)度和最大緩沖區(qū)數(shù)量,就限定了最大的不可回收內(nèi)存數(shù)量,防止高并發(fā)時(shí)緩沖池內(nèi)存持續(xù)增長(zhǎng)
示例:
//創(chuàng)建一個(gè)自定義緩沖池實(shí)例,單個(gè)數(shù)組最大長(zhǎng)度為1024 * 2048,最大可同時(shí)租用10個(gè)緩沖區(qū) ArrayPool<int> CustomerArrayPool = ArrayPool<int>.Create(1024 * 2048,10);
與Shared不同的是,如果設(shè)置 CustomerArrayPool=Null 那么在下一次垃圾回收時(shí)該緩沖池所占的內(nèi)存會(huì)立馬全部釋放。
為防止不可預(yù)測(cè)的風(fēng)險(xiǎn),應(yīng)該保持CustomerArrayPool的存活。
同時(shí)為了防止內(nèi)存的濫用應(yīng)該限制CustomerArrayPool的數(shù)量
到此這篇關(guān)于.Net性能調(diào)優(yōu)-ArrayPool詳情的文章就介紹到這了,更多相關(guān).Net性能調(diào)優(yōu)-ArrayPool內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
.Net執(zhí)行SQL存儲(chǔ)過(guò)程之易用輕量工具詳解
這篇文章主要為大家介紹了.Net執(zhí)行SQL存儲(chǔ)過(guò)程之易用輕量工具詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
ASP.NET Core MVC 依賴(lài)注入View與Controller
本文重點(diǎn)給大家介紹的是ASP.NET Core MVC 之依賴(lài)注入 View 和ASP.NET Core MVC 之依賴(lài)注入 Controller的相關(guān)資料,需要的小伙伴可以參考下面文章具體內(nèi)容2021-09-09
ASP.NET MVC Web API HttpClient簡(jiǎn)介
依稀還記得那個(gè)時(shí)候用WebClient,HttpWebRequest來(lái)發(fā)送一個(gè)請(qǐng)求,現(xiàn)在ASP.NET MVC4中自帶了一個(gè)類(lèi)HttpClient;需要的朋友可以參考下2012-11-11
.NET項(xiàng)目在k8s中運(yùn)行的Dapr持續(xù)集成流程
這篇文章主要介紹了.NET項(xiàng)目在k8s中運(yùn)行的Dapr持續(xù)集成流程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04
一文透徹詳解.NET框架類(lèi)型系統(tǒng)設(shè)計(jì)要點(diǎn)
這篇文章主要為大家透徹詳解了選擇.NET框架的n個(gè)理由,本系列的第一篇文章全面概述了平臺(tái)的支柱和設(shè)計(jì)要點(diǎn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
asp.net mvc路由篇 如何找到 IHttpHandler方法介紹
學(xué)習(xí)是使用asp.net已經(jīng)有很長(zhǎng)一段時(shí)間了,現(xiàn)在就來(lái)分析一下mvc的整過(guò)過(guò)程吧。個(gè)人計(jì)劃寫(xiě)一個(gè)mvc系列的博文,僅從源代碼的角度來(lái)分析mvc。在接觸mvc時(shí)我們一定會(huì)經(jīng)歷路由,那么路由這東東是怎么搞出來(lái)的啊2012-11-11

