内存池
定义
命名空间:TouchSocket.Core
程序集:TouchSocket.Core.dll
一、说明
内存池是TouchSocket
系列的最重要的组成部分,在TouchSocket
产品中,BytePool
贯穿始终。所以熟练使用BytePool
,也是非常重要的。
二、功能
内存池(BytePool)是解决创建大量字节数组消耗的最有力手段,其实质完全借鉴了微软的ArrayPool
。而且在此基础上做了更多的优化。
内存池的最小实现单体是内存块(ByteBlock)
和值内存块(ValueByteBlock)
。ByteBlock
是继承自Stream
的类,拥有和MemoryStream
一样的功能和内存回收管理的增强功能。所以如果有MemoryStream
的使用需求的话,就可以完全让ByteBlock
替代。而ValueByteBlock
是ByteBlock
的值类型(ref struct),其功能除了不继 承Stream以外,和ByteBlock
一模一样。且为值类型,创建开销更小。
三、使用
3.1 创建、回收
BytePool
在默认情况提供了一个Default
的默认实例,当然您可以新建只属于自己的BytePool
。
其中:
- maxArrayLength,是内存池的最大字节数组尺寸。
- maxArraysPerBucket是每个内存块桶的最大数组数量。
- AutoZero属性,在回收内存时,是否清空内存。
- MaxBucketsToTry是最大梯度跨度。例如:当梯度为16、32、64、128、512、1024时,MaxBucketsToTry为2,则当请求的长度是16时,且16的内存块均已派出,则会请求32,最大会请求到64,如果均已派出,则直接新建。
BytePool bytePool = new BytePool(maxArrayLength: 1024 * 1024, maxArraysPerBucket: 50)
{
AutoZero = false,//在回收内存时,是否清空内存
MaxBucketsToTry = 5//最大梯度跨度
};
BytePool defaultBytePool = BytePool.Default;//使用默认的
在内存池创建以后,可以直观的查看它的各个属性。包括:
Console.WriteLine($"内存池容量={bytePool.Capacity}");
Console.WriteLine($"内存池实际尺寸={bytePool.GetPoolSize()}");
使用内存块
ByteBlock
可通过BytePool实例创建,也可以直接new对象,后者使用的是默认内存池实例提供支持。
- byteSize:用于申请的最小字节尺寸。例如:当申请100长度时,可能会得到100,1000,甚至更大尺寸的内存,但绝不会小于100.
ByteBlock byteBlock1 = new ByteBlock(byteSize: 1024 * 1024);//从默认内存池获得
byteBlock1.Dispose();
BytePool bytePool = new BytePool();
ByteBlock byteBlock2 = bytePool.GetByteBlock(byteSize: 1024 * 1024);//从指定内存池获得
using (ByteBlock byteBlock3 = new ByteBlock())//通过using创建及释放时,均在默认内存池
{
}
注意
创建的**ByteBlock(ValueByteBlock)**必须显示释放(Dispose),可用using,如果不释放,虽然不会内存泄露,但是会影响性能。
危险
无论何时何地,都不要直接引用ByteBlock.Buffer,可以直接使用。如果需要引用实际数据,请使用Read、ToArray等方法可复制导出新的数据内存。
3.2 基本数组的写入和读取
基本使用和MemoryStream
一致。
using (var byteBlock = new ByteBlock())
{
byteBlock.Write(new byte[] { 0, 1, 2, 3 });//将字节数组写入
byteBlock.SeekToStart();//将游标重置
var buffer = new byte[byteBlock.Len];//定义一个数组容器
var r = byteBlock.Read(buffer);//读取数据到容器,并返回读取的长度r
}
3.3 基础类型的写入和读取
using (var byteBlock = new ByteBlock())
{
byteBlock.Write(byte.MaxValue);//写入byte类型
byteBlock.Write(int.MaxValue);//写入int类型
byteBlock.Write(long.MaxValue);//写入long类型
byteBlock.Write("RRQM");//写入字符串类型
byteBlock.SeekToStart();//读取时,先将游标移动到初始写入的位置,然后按写入顺序,依次读取
byte byteValue = (byte)byteBlock.ReadByte();
int intValue = byteBlock.ReadInt32();
long longValue = byteBlock.ReadInt64();
string stringValue = byteBlock.ReadString();
}