定义

高性能托管数组缓冲池,可重复使用,用租用空间的方式代替重新分配数组空间的行为

好处

可以在频繁创建和销毁数组的情况下提高性能,减少垃圾回收器的压力

使用

  • 获取缓冲池实例: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的Create()函数会创建一个ConfigurableArrayPool对象

ConfigurableArrayPool的构造函数接收两个参数

  • maxArrayLength:单次租借的数组最大长度,不可超过1024*1024*1024
  • maxArraysPerBucket:最多可以存在的未归还缓冲区数量

通过这两个参数可以解决Shared方式的两个问题:

  1. 自定义单个数组的最大长度,可以获取更大的内存空间用来存储大文件等

  2. 限定了数组的长度和最大缓冲区数量,就限定了最大的不可回收内存数量,防止高并发时缓冲池内存持续增长

示例

//创建一个自定义缓冲池实例,单个数组最大长度为1024 * 2048,最大可同时租用10个缓冲区
ArrayPool<int> CustomerArrayPool = ArrayPool<int>.Create(1024 * 2048,10);

与Shared不同的是,如果设置CustomerArrayPool=Null那么在下一次垃圾回收时该缓冲池所占的内存会立马全部释放。

为防止不可预测的风险,应该保持CustomerArrayPool的存活。

同时为了防止内存的滥用应该限制CustomerArrayPool的数量

.Net性能调优-ArrayPool的更多相关文章

  1. web前端性能调优

    最近2个月一直在做手机端和电视端开发,开发的过程遇到过各种坑.弄到快元旦了,终于把上线了.2个月干下来满满的的辛苦,没有那么忙了自己准备把前端的性能调优总结以下,以方便以后自己再次使用到的时候得于得心 ...

  2. [网站性能2]Asp.net平台下网站性能调优的实战方案

    文章来源:http://www.cnblogs.com/dingjie08/archive/2009/11/10/1599929.html 前言    最近帮朋友运营的平台进行了性能调优,效果还不错, ...

  3. Asp.net平台下网站性能调优的实战方案(转)

    转载地址:http://www.cnblogs.com/chenkai/archive/2009/11/07/1597795.html 前言 最近帮朋友运营的平台进行了性能调优,效果还不错,所以写出来 ...

  4. 第0/24周 SQL Server 性能调优培训引言

    大家好,这是我在博客园写的第一篇博文,之所以要开这个博客,是我对MS SQL技术学习的一个兴趣记录. 作为计算机专业毕业的人,自己对技术的掌握总是觉得很肤浅,博而不专,到现在我才发现自己的兴趣所在,于 ...

  5. sqlserver性能调优第一步

    相信不少的朋友,无论是做开发.架构的,还是DBA等,都经常听说“调优”这个词.说起“调优”,可能会让很多技术人员心头激情澎湃,也可能会让很多人感觉苦恼,不知道如何入手.当然,也有很多人对此不屑一顾,因 ...

  6. JavaScript:内存泄露、性能调优

    1.在进行JS内存泄露检查之前,先要了解JS的内存管理: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Manageme ...

  7. hadoop 性能调优与运维

    hadoop 性能调优与运维 . 硬件选择 . 操作系统调优与jvm调优 . hadoop运维 硬件选择 1) hadoop运行环境 2)  原则一: 主节点可靠性要好于从节点 原则二:多路多核,高频 ...

  8. JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof使用详解

    摘要: JDK本身提供了很多方便的JVM性能调优监控工具,除了集成式的VisualVM和jConsole外,还有jps.jstack.jmap.jhat.jstat.hprof等小巧的工具,本博客希望 ...

  9. websphere性能调优之dump命令

    websphere性能调优之dump命令 基于WebSphere 构建的企业应用,时常会出现性能问题,在严重的情况下还会提示出内存溢出,这是一件很让人恼怒的事情.在WebSphere Applicat ...

随机推荐

  1. MSF+Nmap TCP空闲扫描

    MSF+Nmap TCP空闲扫描 前言 TCP空闲扫描是一种高级的扫描技术,可以冒充内网中另一台IP地址来对内网中的目标进行隐秘的扫描. 正文 在进行扫描之前,我们需要了解一个概念,即递增IP帧标识, ...

  2. Nginx 配置错误导致漏洞

    目录 1.CRLF注入漏洞 2.目录穿越漏洞 参考链接 1.CRLF注入漏洞 CRLF是"回车+换行"(\r\n)的简称,其十六进制编码分别为0x0d和0x0a.先看payload ...

  3. 全网最硬核Handler面试题深度解析

    1.简述Handler的实现原理 Android 应用是通过消息驱动运行的,在 Android 中一切皆消息,包括触摸事件,视图的绘制.显示和刷新等等都是消息.Handler 是消息机制的上层接口,平 ...

  4. 结合场景使用Redis缓存与数据库同步

    Redis缓存与MySQL数据库与同步 什么场景用到了Redis缓存? 1.广告数据 2.搜索时,分类品牌名称,分类名称和规格数据 3.购物车 4.支付 问题:如何实现? 1.广告数据 先查询Redi ...

  5. 一篇文章让你搞懂如何通过Nginx来解决跨域问题

    Nginx跨域实现   首先大家要搞清楚什么是跨域,为什么会有跨域情况的出现.哪些情况属于跨域? 跨域:由于浏览器的同源策略,即属于不同域的页面之间不能相互访问各自的页面内容 注:同源策略,单说来就是 ...

  6. 01-ADB命令获取包名、界面名

    命令代码 adb shell dumpsys window |findstr mFocusedApp

  7. C++进阶—>带你理解多字节编码与Unicode码

    参考网址:https://blog.csdn.net/u011028345/article/details/78516320 多字节字符与宽字节字符 char与wchar_t 我们知道C++基本数据类 ...

  8. CPU 进程 线程 关系与区别

  9. WPF日积月累之TreeView动态绑定

    一.概述 本文演示了如何递归生成数据,用于绑定TreeView以及TreeItem的双击事件. 二.参考代码 1 using System; 2 using System.Collections.Ge ...

  10. C#多线程---Mutex类实现线程同步

    一.例子 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 ...