MemoryStream不能直接当“内存地址”用,因其底层byte[]可能扩容失效、GetBuffer()返回的数组含冗余空间且不保证长期有效,而ToArray()又强制复制;需通过预分配缓冲区或GCHandle.Alloc固定地址才能安全对接非托管API。

MemoryStream 为什么不能直接当“内存地址”用
很多人刚接触 MemoryStream 时,下意识觉得它封装了“一块可读写的内存”,于是尝试用 Marshal 或指针去取它的地址,结果发现 GetBuffer() 返回的数组可能包含未使用的尾部空间,ToArray() 又每次都复制——这不是在操作原始内存,而是在操作一个动态管理的字节容器。
关键点在于:MemoryStream 的底层是 byte[](或 ArraySegment<byte></byte>),但它不保证该数组始终有效、连续、可固定。尤其在多次 Write() 导致扩容后,旧数组会被丢弃,新数组分配,此时之前拿到的引用就失效了。
- 别对
GetBuffer()返回的数组做长期引用,它只在当前MemoryStream未扩容/未 Dispose 时“大概率”安全 -
ToArray()总是返回精确长度的副本,适合导出,但有内存和性能开销 - 如果真需要固定内存地址(如调用非托管 API),应先用
ToArray()或手动分配byte[],再用GCHandle.Alloc(..., GCHandleType.Pinned)固定
如何安全地复用底层 byte[] 避免频繁分配
高频写入场景(比如网络包拼装、日志缓冲)下,反复 ToArray() 会触发 GC 压力。这时可以显式控制缓冲区生命周期:
- 构造时传入预分配的
byte[]:`new MemoryStream(buffer, writable: true)`,这样所有写入都发生在这块数组上 - 写完后用
stream.Position和stream.Length确定有效数据范围,而不是依赖buffer.Length - 若需重用该数组,调用
stream.SetLength(0)清空逻辑长度(不释放数组),再从头写入 - 注意:传入的数组不会被
MemoryStream自动释放,你得自己负责它的生命周期
示例:
byte[] buf = new byte[4096];<br>using var ms = new MemoryStream(buf, writable: true);<br>ms.Write(Encoding.UTF8.GetBytes("hello"));<br>var data = buf.AsSpan(0, (int)ms.Length); // 安全切片MemoryStream.CopyTo 和 WriteAsync 在 Span<byte> 场景下的坑
.NET 5+ 支持 MemoryStream 直接读写 Span<byte></byte>,但默认构造的实例不支持 TryGetBuffer(out MemoryManager<byte>?)</byte>,导致某些底层 API(如 Socket.SendAsync)无法零拷贝传递。
- 若需与
Span<byte></byte>-友好 API 协作,优先使用MemoryStream(byte[])构造,它内部会包装为ArrayMemoryManager -
CopyTo到另一个MemoryStream时,如果目标流容量不足,会自动扩容——但源流的Position不变,容易漏读 -
WriteAsync在同步上下文中(如 ASP.NET Core 同步控制器)可能引发死锁,建议统一用Write+ 手动控制缓冲区,除非明确需要异步 I/O 调度
Dispose 之后还能访问内部数组吗
不能。Dispose 后 MemoryStream 会把内部 byte[] 引用置为 null(除非是外部传入的数组),后续任何读写操作都会抛 ObjectDisposedException。
常见误用:
var ms = new MemoryStream();<br>ms.Write(data);<br>var arr = ms.ToArray();<br>ms.Dispose();<br>// 下面这行会出错:<br>var span = arr.AsSpan(); // arr 还活着,但 ms 已死 —— 这没问题<br>// 但下面这行会出错:<br>ms.Read(buffer, 0, buffer.Length); // ObjectDisposedException
真正危险的是:以为 GetBuffer() 返回的数组能脱离 MemoryStream 独立存活。它只是引用,不是所有权转移。一旦流 Dispose 或内部数组被替换,该引用就不可靠。


















