Microsoft.IO.RecyclableMemoryStream源码解读
一、RecyclableMemoryStreamManager
源码地址:https://github.com/Microsoft/Microsoft.IO.RecyclableMemoryStream
小对象池和大对象池管理、RecyclableMemoryStream创建、各场景的ETW消息\事件钩子;线程安全
备注:官方这张图,只是池块增长策略阐述,不能很好理解其内部池具体实现。小对象还好理解,大对象组织分配并不像画的这样。
1.1、构造函数参数
blockSize:小对象池,块大小;默认128KB。
largeBufferMultiple:大对象池,策略被乘数大小;默认1M。
maximumBufferSize:大对象池,块最大大小;默认128M。
useExponentialLargeBuffer:大对象池策略,true:指数、false:线性;一个类实体只能对应一种策略,如果想使用2种策略,就要定义2个类实体;默认线性。
maximumSmallPoolFreeBytes:byte[] 归还小对象池,小对象池最大大小;超过GC回收,否则归还小对象池;默认不限制。
maximumLargePoolFreeBytes:byte[] 归还大对象池,大对象池最大大小;超过GC回收,否则归还大对象池;默认不限制。
1.2、小对象池
private readonly ConcurrentStack<byte[]> smallPool; //小对象池
private long smallPoolFreeSize; //小对象池归还(空闲)byte大小
private long smallPoolInUseSize; //小对象借出(使用)byte大小
smallPool 使用线程安全堆栈,每个元素bye[] 大小一样,对应blockSize
如:blockSize = 128K,则bye[] 大小都是 128K
maximumSmallPoolFreeBytes = 128M,则代表smallPool 所有块大小总和最大值为128M,超出则归还时不保存到最小池中
1.3、大对象池
private readonly ConcurrentStack<byte[]>[] largePools; //大对象池
private readonly long[] largeBufferFreeSize; //大对象每个层级,归还(空闲)byte大小
private readonly long[] largeBufferInUseSize; //大对象每个层级,借出(使用)byte大小
largePools 使用线程安全堆栈的数组,数组每个索引对应指数/线性一个层级大小,每个层级bye[] 大小一样。
如:largeBufferMultiple = 1M、maximumBufferSize = 128M
则,指数 1M、2M、4M、8M、16M、32M、64M、128M,largePools.Length = 8;largePools[2] 下面bye[] 大小都是4M
线性 1M、2M、3M、4M、5M、6M、7M、......、128M,largePools.Length = 128;largePools[2] 下面bye[] 大小都是3M
largeBufferFreeSize.Length == largePools.Length
largeBufferInUseSize = largePools.Length + 1 ,多出一个元素保存,借出时requiredSize > maximumBufferSize 所有byte[]大小,此byte[] 无法归还到大对象池,会被GC直接回收。
maximumLargePoolFreeBytes = 256M,则代表大池的各个维度块大小总和最大值,超出则归还时不保存到池中,各个维度如:线性有128个维度,指数8个维度,各个维度都是堆栈
线性最大空间:256M * 128 = 32G
指数最大空间:256M * 8 = 2G
1.4、byte[] 借出/归还
借出:
internal byte[] GetBlock() //从小对象池获取byte[],若无则直接创建 new byte[this.BlockSize]
internal byte[] GetLargeBuffer(long requiredSize, Guid id, string tag) //从大对象池获取byte[],首先根据requiredSize计算对应大对象索引位置,若无则直接创建 new byte[requiredSize]
归还:
internal void ReturnBlocks(List<byte[]> blocks, Guid id, string tag) //多个块归还小对象池,判断是否maximumSmallPoolFreeBytes超出,不超出则归还
internal void ReturnBlock(byte[] block, Guid id, string tag) //单个块归还小对象池,判断是否maximumSmallPoolFreeBytes超出,不超出则归还
//归还大对象池,首先根据buffer.Length计算对应大对象索引位置,判断索引对应层级大小是否maximumLargePoolFreeBytes超出,不超出则归还
internal void ReturnLargeBuffer(byte[] buffer, Guid id, string tag)
1.5、RecyclableMemoryStream创建
public MemoryStream GetStream(Guid id, string tag, long requiredSize, bool asContiguousBuffer)
asContiguousBuffer == true && requiredSize > this.BlockSize
请求连续byte[] 且 请求字节大于1个小对象块大小时,则使用大象池创建RecyclableMemoryStream,否则使用小对象池创建RecyclableMemoryStream。
其他重载方法,无asContiguousBuffer参数,默认使用小对象池创建RecyclableMemoryStream。
方法中,byte[] buffer、Memory<byte> buffer、ReadOnlySpan<byte> buffer 参数,会把其中的byte 数据写入新申请的小对象blocks里面,不会复用这些对象。
二、RecyclableMemoryStream
非线程安全
2.1、构造函数
internal RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager, Guid id, string tag, long requestedSize, byte[] initialLargeBuffer)
memoryManager:RecyclableMemoryStreamManager引用,调用其提供借出\归还\通知状态等方法或者属性。
initialLargeBuffer:不为null代表使用大对象池,否则小对象池。GetBuffer()方法也会影响小对象池转为大对象池。
requestedSize:根据请求大小,分配1个大对象池bye[] 或者多个小对象池Block byte[]
2.2、属性
private readonly List<byte[]> blocks = new List<byte[]>(); //如果使用小对象池,则保存借出的多个小对象
private byte[] largeBuffer; //如果使用大对象池,则保存借出的大对象
/*
* 如果使用大对象池,Capacity调整需要更换更大的大对象;
* 老的大对象归还,大对象池超出暂时无法回收大对象,则保存到此,在对象Dispose时再次尝试归还。
* 因为可能有多次此情况发生,所有为List<>
*/
private List<byte[]> dirtyBuffers;
2.3、释放/关闭/析构方法
/// <summary>
/// The finalizer will be called when a stream is not disposed properly.
/// </summary>
/// <remarks>Failing to dispose indicates a bug in the code using streams. Care should be taken to properly account for stream lifetime.</remarks>
~RecyclableMemoryStream()
{
// 析构方法,兜底释放
this.Dispose(false);
}
//非公开方法
/// <summary>
/// Returns the memory used by this stream back to the pool.
/// </summary>
/// <param name="disposing">Whether we're disposing (true), or being called by the finalizer (false).</param>
//disposing 区分是否析构函数调用
protected override void Dispose(bool disposing)
{
if (this.disposed)
{
// 已释放不在释放,记录通知事件
string doubleDisposeStack = null;
if (this.memoryManager.GenerateCallStacks)
{
doubleDisposeStack = Environment.StackTrace;
}
this.memoryManager.ReportStreamDoubleDisposed(this.id, this.tag, this.AllocationStack, this.DisposeStack, doubleDisposeStack);
return;
}
//标记已释放
this.disposed = true;
if (this.memoryManager.GenerateCallStacks)
{
this.DisposeStack = Environment.StackTrace;
}
this.memoryManager.ReportStreamDisposed(this.id, this.tag, this.AllocationStack, this.DisposeStack);
if (disposing)
{
//已释放,不用进入析构队列,不会触发析构函数。
GC.SuppressFinalize(this);
}
else
{
// We're being finalized.
this.memoryManager.ReportStreamFinalized(this.id, this.tag, this.AllocationStack);
//如果此应用程序域正在卸载,并且公共语言运行时已开始调用终止程序,则不执行归还池逻辑。
if (AppDomain.CurrentDomain.IsFinalizingForUnload())
{
// If we're being finalized because of a shutdown, don't go any further.
// We have no idea what's already been cleaned up. Triggering events may cause
// a crash.
base.Dispose(disposing);
return;
}
}
this.memoryManager.ReportStreamLength(this.length);
if (this.largeBuffer != null)
{
//归还大对象
this.memoryManager.ReturnLargeBuffer(this.largeBuffer, this.id, this.tag);
}
if (this.dirtyBuffers != null)
{
//再次尝试归还老的大对象列表
foreach (var buffer in this.dirtyBuffers)
{
this.memoryManager.ReturnLargeBuffer(buffer, this.id, this.tag);
}
}
//归还小对象块列表
this.memoryManager.ReturnBlocks(this.blocks, this.id, this.tag);
this.blocks.Clear();
base.Dispose(disposing);
}
//公共方法
/// <summary>
/// Equivalent to <c>Dispose</c>.
/// </summary>
public override void Close()
{
this.Dispose(true);
}
2.4、小对象使用内部类
标识位置信息,方便参数传递,操作blocks属性。
private struct BlockAndOffset
{
public int Block; //小对象块所在整体位置索引
public int Offset; //小对象块中未使用字节开始位置\已使用字节结束位置
public BlockAndOffset(int block, int offset)
{
this.Block = block;
this.Offset = offset;
}
}
2.5、不连续字节流
public ReadOnlySequence<byte> GetReadOnlySequence()
{
this.CheckDisposed();
if (this.largeBuffer != null)
{
//大对象,只有1个字节数组,连续的
AssertLengthIsSmall();
return new ReadOnlySequence<byte>(this.largeBuffer, 0, (int)this.length);
}
if (this.blocks.Count == 1)
{
//小对象1个块,只有1个字节数组,连续的
AssertLengthIsSmall();
return new ReadOnlySequence<byte>(this.blocks[0], 0, (int)this.length);
}
//小对象多个块,多个字节数据,不连续的
var first = new BlockSegment(this.blocks[0]);
var last = first;
//创建关联下一个块对象
for (int blockIdx = 1; last.RunningIndex + last.Memory.Length < this.length; blockIdx++)
{
last = last.Append(this.blocks[blockIdx]);
}
//首尾对象
return new ReadOnlySequence<byte>(first, 0, last, (int)(this.length - last.RunningIndex));
}
private sealed class BlockSegment : ReadOnlySequenceSegment<byte>
{
public BlockSegment(Memory<byte> memory) => Memory = memory;
public BlockSegment Append(Memory<byte> memory)
{
var nextSegment = new BlockSegment(memory) { RunningIndex = RunningIndex + Memory.Length };
Next = nextSegment;
return nextSegment;
}
}
2.6、IBufferWriter<T>接口实现
private byte[] bufferWriterTempBuffer;
private ArraySegment<byte> GetWritableBuffer(int sizeHint)
{
this.CheckDisposed();
if (sizeHint < 0)
{
throw new ArgumentOutOfRangeException(nameof(sizeHint), $"{nameof(sizeHint)} must be non-negative.");
}
var minimumBufferSize = Math.Max(sizeHint, 1);
this.EnsureCapacity(this.position + minimumBufferSize);
if (this.bufferWriterTempBuffer != null)
{
this.ReturnTempBuffer(this.bufferWriterTempBuffer);
this.bufferWriterTempBuffer = null;
}
if (this.largeBuffer != null)
{
return new ArraySegment<byte>(this.largeBuffer, (int)this.position, this.largeBuffer.Length - (int)this.position);
}
BlockAndOffset blockAndOffset = this.GetBlockAndRelativeOffset(this.position);
int remainingBytesInBlock = this.MemoryManager.BlockSize - blockAndOffset.Offset;
if (remainingBytesInBlock >= minimumBufferSize)
{
//分配小对象,范围属于一个block,则返回block连续段
return new ArraySegment<byte>(this.blocks[blockAndOffset.Block], blockAndOffset.Offset, this.MemoryManager.BlockSize - blockAndOffset.Offset);
}
//分配小对象,单位大于一个block块,则通过大对象/小对象分配byte[];记录赋值给属性bufferWriterTempBuffer;规避不好返回多个blocks中byte
this.bufferWriterTempBuffer = minimumBufferSize > this.memoryManager.BlockSize ?
this.memoryManager.GetLargeBuffer(minimumBufferSize, this.id, this.tag) :
this.memoryManager.GetBlock();
return new ArraySegment<byte>(this.bufferWriterTempBuffer);
}
public void Advance(int count)
{
this.CheckDisposed();
if (count < 0)
{
throw new ArgumentOutOfRangeException(nameof(count), $"{nameof(count)} must be non-negative.");
}
byte[] buffer = this.bufferWriterTempBuffer;
if (buffer != null)
{
if (count > buffer.Length)
{
throw new InvalidOperationException($"Cannot advance past the end of the buffer, which has a size of {buffer.Length}.");
}
//把bufferWriterTempBuffer属性中数据,写回小对象blocks
this.Write(buffer, 0, count);
this.ReturnTempBuffer(buffer);
this.bufferWriterTempBuffer = null;
}
else
{
long bufferSize = this.largeBuffer == null
? this.memoryManager.BlockSize - this.GetBlockAndRelativeOffset(this.position).Offset
: this.largeBuffer.Length - this.position;
if (count > bufferSize)
{
throw new InvalidOperationException($"Cannot advance past the end of the buffer, which has a size of {bufferSize}.");
}
this.position += count;
this.length = Math.Max(this.position, this.length);
}
}
2.7、GetBuffer()
如果使用大对象,则返回大对象数据
如果使用小对象+块1个,则直接返回这个块。如果大于1个,则申请新大对象返回,之前小对象归还。升级为使用大对象。
2.8、ToArray()
通过new byte[this.Length] 创建新byte数组,拷贝大对象/小对象数据过来。
Microsoft.IO.RecyclableMemoryStream源码解读的更多相关文章
- java.lang.system 类源码解读
通过每块代码进行源码解读,并发现源码使用的技术栈,扩展视野. registerNatives 方法解读 /* register the natives via the static initializ ...
- SDWebImage源码解读之SDWebImageCache(下)
第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...
- 源码解读—HashTable
在上一篇学习过HashMap(源码解读—HashMap)之后对hashTable也产生了兴趣,随即便把hashTable的源码看了一下.和hashMap类似,但是也有不同之处. public clas ...
- String、StringBuffer、StringBuilder源码解读
序 好长时间没有认真写博客了,过去的一年挺忙的.负责过数据库.线上运维环境.写代码.Code review等等东西挺多. 学习了不少多方面的东西,不过还是需要回归实际.加强内功,方能扛鼎. 去年学习M ...
- SDWebImage源码解读之SDWebImageManager
第九篇 前言 SDWebImageManager是SDWebImage中最核心的类了,但是源代码确是非常简单的.之所以能做到这一点,都归功于功能的良好分类. 有了SDWebImageManager这个 ...
- SDWebImage源码解读之干货大总结
这是我认为的一些重要的知识点进行的总结. 1.图片编码简介 大家都知道,数据在网络中是以二进制流的形式传播的,那么我们该如何把那些1和0解析成我们需要的数据格式呢? 说的简单一点就是,当文件都使用二进 ...
- HttpClient 4.3连接池参数配置及源码解读
目前所在公司使用HttpClient 4.3.3版本发送Rest请求,调用接口.最近出现了调用查询接口服务慢的生产问题,在排查整个调用链可能存在的问题时(从客户端发起Http请求->ESB-&g ...
- php-msf 源码解读【转】
php-msf: https://github.com/pinguo/php-msf 百度脑图 - php-msf 源码解读: http://naotu.baidu.com/file/cc7b5a49 ...
- swoft 源码解读【转】
官网: https://www.swoft.org/ 源码解读: http://naotu.baidu.com/file/814e81c9781b733e04218ac7a0494e2a?toke ...
- go语言nsq源码解读八 http.go、http_server.go
这篇讲另两个文件http.go.http_server.go,这两个文件和第六讲go语言nsq源码解读六 tcp.go.tcp_server.go里的两个文件是相对应的.那两个文件用于处理tcp请求, ...
随机推荐
- 安装配置docker&maven环境
原文视频:(https://blog.sechelper.com/20220919/code-review/docker-maven-install-guid/) Docker是什么 Docker ...
- 【项目实战】CNN手写识别
由于只需要修改之前基于ANN模型代码的模型设计部分所以篇幅较短,简单的加点注释给自己查看即可 视频链接:https://www.bilibili.com/video/BV1Y7411d7Ys?p=10 ...
- Goland Socket 服务
客户端发送消息 并接收服务端消息 package main import ( "fmt" "net" ) func main() { // conn, err ...
- UML类中的6种关系
最近现看Java设计模式,但是越看越不明白,一直搞不明白类与类之前的关系有哪些,通过几天的学习与整理通过代码实现的UML画图的方式理解类之间的相互关系. 一.类与类6大关系: 泛化(generaliz ...
- C言语语法总结(随时更新)
一.gcc1. gcc xxx.c -o xxx #把原代码编译成可执行文件xxx2. gcc -c xxx.c #编译: 把原代码编译xxx.o后辍的目标文件3. gcc xxx.o -o xxx ...
- 利用Hutool-(Java工具类)实现验证码校验
目录 Hutool工具类介绍 Hutool实现验证码生成 测试验证码生成 其他样式的验证码 第一篇是纯利用现有JDK提供的绘图类(ImageIO)类制作,这个过程比较复杂且需要了解ImageIO类. ...
- 18.MongDB系列之批量更新写入Python版
在实际的工作中,难免批量更新的数量极大,pymongo提供了便捷的客户端供使用 假设读者对pandas比较熟悉,下图为事先准备好的dataframe import pandas as pd from ...
- 小程序返回上一级页面背景音乐报错 setBackgroundAudioState:fail title is nil!;
小程序初始化在onLoad的时候加载了一次背景音乐. 如果此时报错是title必传.如果没有 会报错一次 setBackgroundAudioState:fail title is nil!; 这个都 ...
- JSP中实现留言页面的编写并将留言信息展示出来
1.JavaBean类,实现java代码和html的部分分离,提高代码的复用 package com.wgh; public class MessageBean { private String au ...
- Java模拟生产者-消费者问题。生产者不断的往仓库中存放产品,消费者从仓库中消费产品。其中生产者和消费者都可以有若干个。在这里,生产者是一个线程,消费者是一个线程。仓库容量有限,只有库满时生产者不能存
需求分析:生产者生产产品,存放在仓库里,消费者从仓库里消费产品. 程序分析: 1.生产者仅仅在仓储未满时候生产,仓满则停止生产. 2.消费者仅仅在仓储有产品时候才能消费,仓空则等待. 3.当消费者发现 ...