每日一题:Span<T>和Memory<T>
每日一题:Span<T>和Memory<T>
参考答案:
在 .NET 中,Span<T> 和 Memory<T> 是用于高性能内存操作的重要结构,它们主要解决了 减少内存分配和提高数据处理效率的问题。
Span<T> 是一种表示 连续内存区域的轻量级结构(ref struct)。它可以指向数组、栈内存或非托管内存的一部分,而不需要复制数据。例如,一个 Span<byte> 可以表示数组中的某一段数据。
Span<T> 的主要特点包括:
不会分配新的内存
可以安全地操作连续内存片段
提高性能并减少 GC 压力
由于 Span<T> 是 ref struct,它只能存在于 栈上,因此不能被装箱、不能作为类字段,也不能在 async 方法中使用。
Memory<T> 则是 Span<T> 的 可堆分配版本。它可以存储在堆上,并可以跨越异步方法或长期存储。当需要在异步操作或类成员中保存内存引用时,通常使用 Memory<T>。
简单理解:
Span<T>:高性能、栈上、短生命周期
Memory<T>:可在堆上存储、适合异步场景
这两个类型在 高性能网络库、序列化框架、IO 处理等场景中非常常见。
追问 1
为什么 Span<T> 不能在 async 方法中使用?
答案:
Span<T> 是一种 ref struct 类型,它只能存储在栈上,而不能被移动到托管堆中。async 方法在编译后会被转换为 状态机对象,该状态机通常存储在堆上,以便在异步操作完成后恢复执行。
如果 Span<T> 能够跨越 await 存在,就意味着它必须被存储在状态机对象中,这会导致它被移动到堆上,从而违反 Span<T> 的设计规则。因此,C# 编译器禁止 Span<T> 在 async 方法中跨越 await 使用。
这也是为什么在异步场景中通常使用 Memory<T> 或 ReadOnlyMemory<T>。
追问 2
为什么 Span<T> 能提高性能?
答案:
Span<T> 提高性能的主要原因是它 避免了额外的内存分配和数据复制。在传统代码中,如果需要处理数组的一部分数据,通常需要创建新的数组或使用 Array.Copy,这会导致额外的内存分配。
而 Span<T> 只是对现有内存的一种 视图(view),它不会复制数据,而是直接引用原始内存区域。因此,程序可以在不分配新内存的情况下操作数据。
此外,减少内存分配也意味着 降低 GC 压力,从而提高整体性能。这也是为什么在高性能库(如网络框架、序列化库)中广泛使用 Span<T>。
#面试题 #dotnet面试题 #面试真题
