在.NET Core 3.0项目中,我有一个返回Span<byte>
的接口。这适用于一大组类,除了一个特定的实现,它可以动态生成其数据(由于没有缓存它(。
实现如下所示:
public Span<byte> Data => CompileBytes();
它会像这样(这是抽象代码,但非常接近用例(
public byte[] CompileBytes()
{
using (MemoryStream stream = new MemoryStream())
{
foreach (IDataSource data in DataSources)
stream.Write(data.ByteArray);
return stream.ToArray();
}
}
我一直在网上寻找,看看是否有保证这是安全的,但没有找到。
我担心的是,Span
是GC将忽略的数据周围的非常薄的一层,因此GC假设我们不会让跨度超过底层缓冲区,并且创建的临时字节数组最终将获得GC'd,这意味着如果由于某种原因,当其他代码正在使用跨度时,我可能会有一个滴答作响的定时炸弹。是这样吗?我可以为临时对象返回一个Span<>
并且完全没问题(假设它通过保持在跨度的范围内被正确使用(?
该定义似乎依赖于实现,因此我无法以我有限的知识弄清楚它是否保留了引用......因为如果是这样,那么我就安全了,我的问题得到了回答。
MSDN 说"内存安全",但我不确定他们定义内存安全的确切细节以及它是否涵盖我的定义。因此,如果是这样,那么这个问题就得到了回答。
我没有使用任何unsafe
代码。
在Span<T>
中引用托管数组是安全的,即使它是对它的唯一引用。
如文章 关于跨度:探索新的 .NET 支柱中所述,Span<T>
使用一种特殊的方式来存储这些引用,即ByReference<T>
,它作为 JIT 内部函数实现。
引用链接的文章(如何实现Span<T>
部分?
Span<T>
实际上是为了在运行时中使用一个特殊的内部类型而编写的,该类型被视为实时 (JIT( 内部类型,JIT 为其生成相当于 ref T 字段
和部分什么是Memory<T>
,你为什么需要它?
Span<T>
是一种类似 ref 的类型,因为它包含一个 ref 字段,并且 ref 字段不仅可以引用数组等对象的开头,还可以引用它们的中间 [...]这些引用称为内部指针,对于 .NET 运行时的垃圾回收器来说,跟踪它们是一项相对昂贵的操作。
该引用的最后一部分阐明了存储在Span<T>
中的引用确实由 GC 跟踪,因此它不会清理仍在引用的内存