.Net性能调优-ArrayPool
时间:2021-09-13
本文章向大家介绍.Net性能调优-ArrayPool,主要包括.Net性能调优-ArrayPool使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
定义
高性能托管数组缓冲池,可重复使用,用租用空间的方式代替重新分配数组空间的行为
好处
可以在频繁创建和销毁数组的情况下提高性能,减少垃圾回收器的压力
使用
- 获取缓冲池实例:Create/Shared
var pool=ArrayPool[byte].Shared
- 调用缓冲池实例Rent()函数,租用缓冲区空间
byte[] array=pool.Rent(1024)
- 调用缓冲池实例Return(array[T])函数,归还租用的空间
pool.Return(array)
Shared
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;
}
特点
- 租用数组长度不可超过 2^20( 1024*1024 = 1 048 576),否则会从GC中重新开辟内存空间
- Rent租用数组实际返回的长度可能比请求的长度大,返回长度一是(16*2^n)
- Return归还缓冲区的时候,如果不设置clearArray,下一个租用者可能会看到之前的填充的值(在返回的数组长度刚好是下一个租用者请求的长度时会被看到)
- 缓冲池的内存释放不是实时释放,在缓冲区空闲时,大概10到20秒之后,会随着第2代GC一起释放,分批释放
- 并发数量持续增长时,缓冲池占用的内存空间也会持续增长,而且似乎没有上限
耗时对比
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
示例(前端文件通过后端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);
}
Create()
ArrayPool
ConfigurableArrayPool
- maxArrayLength:单次租借的数组最大长度,不可超过
1024*1024*1024
- maxArraysPerBucket:最多可以存在的未归还缓冲区数量
通过这两个参数可以解决Shared方式的两个问题:
-
自定义单个数组的最大长度,可以获取更大的内存空间用来存储大文件等
-
限定了数组的长度和最大缓冲区数量,就限定了最大的不可回收内存数量,防止高并发时缓冲池内存持续增长
示例
//创建一个自定义缓冲池实例,单个数组最大长度为1024 * 2048,最大可同时租用10个缓冲区
ArrayPool<int> CustomerArrayPool = ArrayPool<int>.Create(1024 * 2048,10);
与Shared不同的是,如果设置CustomerArrayPool=Null
那么在下一次垃圾回收时该缓冲池所占的内存会立马全部释放。
为防止不可预测的风险,应该保持CustomerArrayPool的存活。
同时为了防止内存的滥用应该限制CustomerArrayPool的数量
原文地址:https://www.cnblogs.com/bluesummer/p/15263871.html
- Selenium2+python自动化44-元素定位参数化(find_element)
- Selenium2+python自动化45-18种定位方法(find_elements)
- Python做文本挖掘的情感极性分析(基于情感词典的方法)
- Selenium2+python自动化42-判断元素(expected_conditions)
- 基于机器学习的文本情感极性分析
- Selenium2+python自动化43-判断title(title_is)
- hihoCoder #1142 : 三分求极值
- 容斥原理
- TensorFlow:TensorBoard可视化
- Codeforces 768B Code For 1
- 【干货】深入理解自编码器(附代码实现)
- SlopOne推荐算法(附Python源码)
- 后缀数组(一堆干货)
- POJ 1741 Tree(树的点分治,入门题)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- sed 实用实例参考
- 微信小程序WXML页面常用语法(讲解+示例)
- Golang领域模型开篇,当Go遇上DDD
- Spring事务专题(五)聊聊Spring事务到底是如何实现的
- 深色模式适配指南
- 【Flutter 专题】97 仿网易新闻标签选择器
- 宋宝华:论Linux的页迁移(Page Migration)完整版
- 三、玩转Git三剑客-Git与Github的简单同步
- 四、玩转Git三剑客-Git多人单分支集成协作时的常见场景
- 准时下班的秘密:集成 GitLab && JIRA 实现自动化 workflow
- ZLT-MP v4.1.0 发布
- 高并发系统三大利器之限流
- XtraBackup工具详解 Part 2 xtrabackup安装
- XtraBackup工具详解 Part 4 XtraBackup权限及配置
- XtraBackup工具详解 Part 5 使用innobackupex对数据库进行全备