.Net性能调优-ArrayPool详情

 更新时间:2021年09月14日 11:02:04   作者:张三~~  
ArrayPool具有高性能 托管 数组缓冲池,可重复使用,用 租用 空间的方式代替 重新分配 数组空间的行为的特点及可以在频繁创建和销毁数组的情况下 提高性能 ,减少垃圾回收器的压力的优点,下面文章内容将详细对其做介绍,需要的朋友可以参考一下

1、使用

  • 获取缓冲池实例 :Create / Shared var pool=ArrayPool[byte].Shared
  • 调用缓冲池实例 :Rent() 函数,租用缓冲区空间 byte[] array=pool.Rent(1024)
  • 调用缓冲池实例 :Return(array[T]) 函数,归还租用的空间 pool.Return(array)

2、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.1特点

租用数组长度不可超过 2^20( 1024*1024 = 1 048 576),否则会从GC中重新开辟内存空间
Rent 租用数组实际返回的长度可能比请求的长度大,返回长度一是(16*2^n)
Return
归还缓冲区的时候,如果不设置 clearArray ,下一个租用者可能会看到之前的填充的值(在返回的数组长度刚好是下一个租用者请求的长度时会被看到)
缓冲池的内存释放不是实时释放,在缓冲区空闲时,大概10到20秒之后,会随着第2代GC一起释放,分批释放
并发数量持续增长时,缓冲池占用的内存空间也会持续增长,而且似乎没有上限

2.2耗时对比

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

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()函数会创建一个 ConfigurableArrayPool 对象

ConfigurableArrayPool 构造函数接收两个参数

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

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

  • 自定义单个数组的最大长度,可以获取更大的内存空间用来存储大文件等
  • 限定了数组的长度和最大缓冲区数量,就限定了最大的不可回收内存数量,防止高并发时缓冲池内存持续增长

示例:

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


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

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

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

到此这篇关于.Net性能调优-ArrayPool详情的文章就介绍到这了,更多相关.Net性能调优-ArrayPool内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • .Net执行SQL存储过程之易用轻量工具详解

    .Net执行SQL存储过程之易用轻量工具详解

    这篇文章主要为大家介绍了.Net执行SQL存储过程之易用轻量工具详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • ASP.NET Core  依赖注入框架的使用

    ASP.NET Core 依赖注入框架的使用

    还记得上篇文章中最后提及到,假如服务越来越多怎么处理呢,本篇文章将会带来解决办法, ASP.NET Core 依赖注入框架的相关资料,需要的小伙伴可以参考下面文章的具体内容
    2021-10-10
  • ASP.NET Core MVC 依赖注入View与Controller

    ASP.NET Core MVC 依赖注入View与Controller

    本文重点给大家介绍的是ASP.NET Core MVC 之依赖注入 View 和ASP.NET Core MVC 之依赖注入 Controller的相关资料,需要的小伙伴可以参考下面文章具体内容
    2021-09-09
  • ASP.NET MVC Web API HttpClient简介

    ASP.NET MVC Web API HttpClient简介

    依稀还记得那个时候用WebClient,HttpWebRequest来发送一个请求,现在ASP.NET MVC4中自带了一个类HttpClient;需要的朋友可以参考下
    2012-11-11
  • .NET项目在k8s中运行的Dapr持续集成流程

    .NET项目在k8s中运行的Dapr持续集成流程

    这篇文章主要介绍了.NET项目在k8s中运行的Dapr持续集成流程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • 一文透彻详解.NET框架类型系统设计要点

    一文透彻详解.NET框架类型系统设计要点

    这篇文章主要为大家透彻详解了选择.NET框架的n个理由,本系列的第一篇文章全面概述了平台的支柱和设计要点,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • ASP.NET Core 依赖注入详细

    ASP.NET Core 依赖注入详细

    这篇文章主要介绍ASP.NET Core 依赖注入,ASP.NET Core 应用在启动过程中会依赖各种组件提供服务,这些组件会以接口的形式标准化,这些组件就是我们所说的服务,ASP.NET Core框架建立在一个底层的依赖注入框架之上,它使用容器提供所需的服务,下面我们就来详细了解一下
    2021-10-10
  • .NET6新特性之 隐式命名空间引用

    .NET6新特性之 隐式命名空间引用

    本文给大家分享的是 .NET6特新 隐式命名空间引用,如果我们要在新加一个命名空间的引用,可以在项目文件中配置增加<Using Include="命名空间"/>,如果需要移除一个命名空间可以这么做<Using Remove="命名空间"/>,下面来看看文章详细介绍内容吧,需要的朋友可以参考一下
    2021-11-11
  • .NET 6 即将到来的新特性  隐式命名空间引用

    .NET 6 即将到来的新特性 隐式命名空间引用

    ASP.NET 现在我们还是需要手动加命名空间引用,在以后的版本中可能就不需要手动加命名空间的引用了,本文就来介绍.NET 6即将到来的新特性--隐式命名空间引用,,需要的朋友可以参考下面文章内容
    2021-09-09
  • asp.net mvc路由篇 如何找到 IHttpHandler方法介绍

    asp.net mvc路由篇 如何找到 IHttpHandler方法介绍

    学习是使用asp.net已经有很长一段时间了,现在就来分析一下mvc的整过过程吧。个人计划写一个mvc系列的博文,仅从源代码的角度来分析mvc。在接触mvc时我们一定会经历路由,那么路由这东东是怎么搞出来的啊
    2012-11-11

最新评论