.Net性能調(diào)優(yōu)-ArrayPool詳情
1、使用
- 獲取緩沖池實例 :
Create / Shared var pool=ArrayPool[byte].Shared
- 調(diào)用緩沖池實例 :
Rent()
函數(shù),租用緩沖區(qū)空間byte[] array=pool.Rent(1024)
- 調(diào)用緩沖池實例 :
Return(array[T])
函數(shù),歸還租用的空間pool.Return(array)
2、Shared
Shared
返回為一個靜態(tài)共享實例,實際返回了一個 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特點
租用數(shù)組長度不可超過 2^20( 1024*1024 = 1 048 576),
否則會從GC中重新開辟內(nèi)存空間
Rent
租用數(shù)組實際返回的長度可能比請求的長度大,返回長度一是(16*2^n)
歸還緩沖區(qū)的時候,如果不設(shè)置
Return clearArray
,下一個租用者可能會看到之前的填充的值(在返回的數(shù)組長度剛好是下一個租用者請求的長度時會被看到)
緩沖池的內(nèi)存釋放不是實時釋放,在緩沖區(qū)空閑時,大概10到20秒之后,會隨著第2代GC一起釋放,分批釋放
并發(fā)數(shù)量持續(xù)增長時,緩沖池占用的內(nèi)存空間也會持續(xù)增長,而且似乎沒有上限
2.2耗時對比
private static void TimeMonitor() { //隨機生成3000個數(shù)組的長度值 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ù)組耗時:" + time1 + " Gc總分配量" + (gcAllocate1 - gcAllocate0)); Console.WriteLine("默認方式創(chuàng)建數(shù)組耗時:" + time2 + " Gc總分配量" + (gcAllocate2 - gcAllocate1 - gcAllocate0)); }
內(nèi)存使用截圖:左側(cè)沒有波動的橫線是緩沖池執(zhí)行的過程,右側(cè)為手動創(chuàng)建數(shù)組的執(zhí)行過程
執(zhí)行結(jié)果:
ArrayPool方式創(chuàng)建數(shù)組耗時:17545 Gc總分配量4130800
默認方式創(chuàng)建數(shù)組耗時:26870 Gc總分配量37354100896
2.3示例(前端文件通過后端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ù)會創(chuàng)建一個 ConfigurableArrayPool
對象
ConfigurableArrayPool
的構(gòu)造函數(shù)接收兩個參數(shù)
- maxArrayLength :單次租借的數(shù)組最大長度,不可超過
1024*1024*1024
- maxArraysPerBucket :最多可以存在的未歸還緩沖區(qū)數(shù)量
通過這兩個參數(shù)可以解決 Shared
方式的兩個問題:
- 自定義單個數(shù)組的最大長度,可以獲取更大的內(nèi)存空間用來存儲大文件等
- 限定了數(shù)組的長度和最大緩沖區(qū)數(shù)量,就限定了最大的不可回收內(nèi)存數(shù)量,防止高并發(fā)時緩沖池內(nèi)存持續(xù)增長
示例:
//創(chuàng)建一個自定義緩沖池實例,單個數(shù)組最大長度為1024 * 2048,最大可同時租用10個緩沖區(qū) ArrayPool<int> CustomerArrayPool = ArrayPool<int>.Create(1024 * 2048,10);
與Shared不同的是,如果設(shè)置 CustomerArrayPool=Null
那么在下一次垃圾回收時該緩沖池所占的內(nèi)存會立馬全部釋放。
為防止不可預(yù)測的風險,應(yīng)該保持CustomerArrayPool
的存活。
同時為了防止內(nèi)存的濫用應(yīng)該限制CustomerArrayPool
的數(shù)量
到此這篇關(guān)于.Net性能調(diào)優(yōu)-ArrayPool
詳情的文章就介紹到這了,更多相關(guān).Net性能調(diào)優(yōu)-ArrayPool
內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用ASP.NET技術(shù)動態(tài)生成HTML頁面
利用ASP.NET技術(shù)動態(tài)生成HTML頁面...2006-07-07