news 2026/5/1 9:14:56

ArrayPool.Shared解说

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ArrayPool.Shared解说

NET 中频繁创建和销毁数组的情况下会导致垃圾回收器出现严重的内存压力,ArrayPool<T> 通过池化手段有效地降低了数组的分配和垃圾回收器的回收压力,同时 ArrayPool<T> 也是 MemoryPool<T> 和 PipeWriter、PipeReader 的底板。

ArrayPool<T>.Shared 是 ArrayPool<T> 的一种实现,它设计成静态共享以供全体共同使用。在实际应用中, Shared 实例几乎承载了全部的 ArrayPool<T> 调用。

Shared实例的Rent#

Copy

static void RentAndReturn(int size)

{

var pool = ArrayPool<byte>.Shared;

var array = pool.Rent(size);

// 在这里使用 array

pool.Return(array);

}

其中输入的 size 和 Rent 得到 array.Length的关系如下:

Copy

var index = SelectBucketIndex(size);

var arrayLenth = GetMaxSizeForBucket(index);

static int SelectBucketIndex(int size)

{

return BitOperations. Log2((uint)((size - 1) | 0xF)) - 3;

}

static int GetMaxSizeForBucket(int bucketIndex)

{

return 16 << bucketIndex;

}

调用 SelectBucketIndex(int.MaxValue) 会得到27,实际上 Shared 实现只维护最大索引值为 26 的总共 27 个 Bucket,所以当你 Rent 出大于 1GB 的数组时永远触发分配且不会 Return 到 Bucket 里。

Shared实例的Return#

如果我们书写如下代码,会发生什么情况?

Copy

var pool = ArrayPool<byte>.Shared;

var array1 = new byte[16];

pool.Return(array1);

var array2 = new byte[16];

pool.Return(array2);

array1 和 array2 都不是 Rent 出来的,Return 操作 pool 会接受吗?

答案是都接受,只要 array.Length 符合要求都会接受,而且也不会考虑多次 Return 同一个引用 array 的特殊情况。所以千万不要对同一个 Rent 到的 array 实例进行多次 return 操作。

Shared实例的Thread-Local-Storage#

如果我们书写如下代码,会触发 Shared 的 Buckets 读写访问吗?

Copy

var pool = ArrayPool<byte>.Shared;

for(var i =0; i <100; i++)

{

var array = pool.Rent(1024 * 1024);

pool.Return(array);

}

答案是不会触发 Buckets 读写访问,执行之后 27 个 Bucket 还是未初始化的 null 值,因为Shared使用Thread-Local-Storage做了第一层无锁缓存。

原始代码

Copy

[ThreadStatic]

private static SharedArrayPoolThreadLocalArray[]? t_tlsBuckets;

方便理解的简化代码

Copy

// 创建一个 Array 类型的数组,数组共27个元素

// 每个元素的索引值,对应 Bucket 的索引

[ThreadStatic]

private static Array? [] t_tlsBuckets = new Array[27];

当执行

Copy

var array = ArrayPool<byte>.Shared.Rent(16);

实际的逻辑是

Copy

var index = SelectBucketIndex(16); // index = 0;

var array = t_tlsBuckets[index];

if(array != null)

{

t_tlsBuckets[index] = null; // 当然这里不会对t_tlsBuckets进行2次索引访问,使用 ref 来解决

return array;

}

Return 的时候也是一样的缓存访问逻辑,当 t_tlsBuckets 索引对应的元素为 null 时缓存成功。

Shared实例的Buckets#

当 t_tlsBuckets[index] 缓存不命中时,会触发 Bucket = Buckets[index] 的创建和访问。注意这里一个 Bucket 不是 Array的集合,而是Array的集合的集合,我们叫他Partitions。为了方便理解,下面给出示意代码:

Copy

// bucket 是 Partition 的集合,Partition 是 Array 的集合

// bucket 的元素数量是 Environment.ProcessorCount

List<Partition> bucket = Buckets[index];

Debug.Assert(bucket.Count == Environment.ProcessorCount);

当进行 Rent 操作时,尝试从 bucket 里弹出一个 Array,其示意过程如下(注意真实的实现为高性能代码可理解性更难):

Copy

// bucket 是 Partition 的集合,Partition 是 Array 的集合

// bucket 的元素数量是 Environment.ProcessorCount

List<Partition> bucket = Buckets[index];

// 每个 cpu 核心操作固定的一个 Partition,目的是去锁化

int cpuIndex = Thread.GetCurrentProcessorId() % Environment.ProcessorCount;

Partition cpuPartition = bucket[cpuIndex];

// Partition 的元素数量固定是 32

Debug.Assert(cpuPartition.Count == 32);

// 尝试从当前 cpu 核心对应的 Partition 进行TryPop()

// TryPop() 的实现有排他锁,但这里只要没有别的线程来访问此 Partition,就不会产生互斥

// 如果在同一线程下租赁过32次且不归还,TryPop()会失败

Array array = null;

if ((array = cpuPartition.TryPop()) != null)

{

return array;

})

// 以上失败后则尝试从其它的 Partition TryPop(),此时排他锁可能就生效了

for(var index = 0; index < bucket.Count; index++)

{

if(index!= cpuIndex)

{

Partition otherPartition = bucket[index];

if ((array = otherPartition.TryPop()) != null)

{

return array;

})

}

}

return null;

在进行 Return 操作是,大的流程和 Rent 一样,不同是 TryPop() 变成了TryPush()。

总结一下,在指定 size 之后,Shared 实例最多能 Rent(size) 出 Environment.ProcessorCount * 32 个可复用的数组,伴随着 Return 的及时性越低,Rent 时排它锁触发的几率就越高。

总结#

以为是本人对 ArrayPool.Shared 实现的分析和理解,并变换成一种相对容易理解的伪代码,如有不对之处请指正。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 9:10:00

VonaJS提供的读写分离,直观,优雅[特殊字符]

在VonaJS中实现读写分离&#xff0c;只需提供一组写数据源和一组读数据源。当用户访问后端 API 时&#xff0c;系统会按照规则自动选择写数据源或读数据源&#xff0c;访问相应的数据库&#xff0c;从而分摊压力&#xff0c;提升系统性能安装模块读写分离作为独立的模块提供&am…

作者头像 李华
网站建设 2026/5/1 9:08:02

鸿蒙6.0:AI与智能体框架(HMAF),重塑操作系统未来的核心密码

当用户说出“帮我规划带老人孩子的周末短途游”&#xff0c;系统便能自动整合行程、餐饮、景点资源生成完整方案&#xff1b;当驾车抵达加油站&#xff0c;车载系统自动识别油枪并完成人脸支付&#xff1b;当需要分析Excel数据&#xff0c;仅凭自然语言就能完成复杂报表生成——…

作者头像 李华
网站建设 2026/5/1 9:11:22

【往届均已成功见刊检索、早鸟优惠】第六届计算机网络安全与软件工程国际学术会议(CNSSE 2026)

第六届计算机网络安全与软件工程国际学术会议&#xff08;CNSSE 2026&#xff09;将于2026年2月6-8日在中国-青岛举行。CNSSE 2026专注于计算机网络安全、软件工程、信号处理、程序分析等领域&#xff0c;致力于搭建计算机领域学术资源共享平台&#xff0c;扩大国际科研学术合作…

作者头像 李华
网站建设 2026/5/1 9:14:39

你必须知道的TCP和UDP核心区别,快速搞懂这两大协议!

1. TCP (Transmission Control Protocol)概念TCP&#xff08;传输控制协议&#xff09;是一种面向连接的、可靠的传输协议。它负责将数据从源主机传输到目标主机&#xff0c;并确保数据的完整性、顺序和正确性。原理三次握手&#xff1a;在数据传输之前&#xff0c;TCP协议通过…

作者头像 李华
网站建设 2026/4/29 21:49:00

什么是智能体工程Agent Engineering?

文章介绍了智能体工程这一新兴领域&#xff0c;它是将不可靠的大模型系统转化为生产环境稳定应用的方法论。强调构建、测试、上线、观察、优化的循环过程&#xff0c;需要产品思维、工程能力和数据科学的配合。与传统开发不同&#xff0c;智能体工程将生产环境视为最佳学习场所…

作者头像 李华
网站建设 2026/4/26 19:01:52

基于大数据的热点话题分析系统的设计与实开题报告 (1)

毕业论文&#xff08;设计&#xff09;开题报告题 目基于大数据的热点话题分析系统的设计与实现学 院专 业年 级开题日期学 号姓 名指导教师选题的目的、意义、研究现状&#xff0c;本选题研究的基本内容、拟解决的主要问题&#xff1a;1、选题的目的和意义基于大数据的热…

作者头像 李华