You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#中测量UTF8编码字符串相对UTF16的内存占用收益

验证ReadOnlySpan<byte>与string的内存占用差异

首先,你遇到的Marshal.SizeOf()报错翻译为:

类型‘System.String’无法作为非托管结构进行封送处理;无法计算有意义的大小或偏移量。

这个报错的核心原因是:Marshal.SizeOf()仅适用于非托管类型或可直接封送的托管类型,而.NET中的string是带有CLR头部(同步块索引、类型指针、长度等元数据)的托管对象,并非简单的字节序列,因此无法用该工具获取实际内存占用。

下面是几种直观验证两者内存差异的方法:

1. 直接计算编码字节数+对象开销

  • string utf16 = "ab→cd":.NET字符串默认采用UTF-16编码,每个字符占2字节。这段字符串包含5个Unicode字符(a、b、→、c、d),内容本身占 5×2=10 字节。加上64位系统下string的固定头部开销(24字节:8字节同步块索引+8字节类型指针+4字节长度+4字节预留),总内存占用为34字节,CLR会按8字节对齐规则分配,实际占用40字节左右。
  • ReadOnlySpan<byte> utf8 = "ab→cd"u8:UTF-8编码下,a/b/c/d各占1字节,→(U+2192)占3字节,内容总字节数为 1+1+3+1+1=7 字节。而ReadOnlySpan<byte>是值类型,在栈上仅占16字节(64位系统:8字节指针+8字节长度),它指向编译时嵌入程序的静态只读UTF-8字节数组(该数组内存仅占用7字节,对齐后为8字节)。由于Span本身不持有内存,仅作为引用,整体内存开销远小于string。

2. 通过GC.GetTotalMemory观察内存增量

通过创建大量实例对比内存变化,能直观看到差异:

// 先执行Full GC,获取初始内存基准
GC.Collect();
GC.WaitForPendingFinalizers();
long initialMemory = GC.GetTotalMemory(true);

const int instanceCount = 1000000;
// 测试string内存占用(避免字符串驻留,用动态生成的字符串)
var stringArray = new string[instanceCount];
for (int i = 0; i < instanceCount; i++)
{
    stringArray[i] = "ab→cd" + i.ToString();
}
long stringMemory = GC.GetTotalMemory(true) - initialMemory;
Console.WriteLine($"100万个string实例的内存增量:{stringMemory:N0} 字节");

// 清理string并重新GC
Array.Clear(stringArray);
GC.Collect();
GC.WaitForPendingFinalizers();
initialMemory = GC.GetTotalMemory(true);

// 测试ReadOnlySpan<byte>内存占用
var spanArray = new ReadOnlySpan<byte>[instanceCount];
for (int i = 0; i < instanceCount; i++)
{
    spanArray[i] = "ab→cd"u8;
}
long spanMemory = GC.GetTotalMemory(true) - initialMemory;
Console.WriteLine($"100万个ReadOnlySpan实例的内存增量:{spanMemory:N0} 字节");

运行后会发现,string的内存增量是Span的数十倍——因为每个string都是独立的托管对象,而Span数组里仅存指向同一段静态数组的引用,几乎没有额外堆内存开销。

3. 用调试工具查看内存布局

在Visual Studio中开启「内存」窗口:

  • 定位到string对象地址,前24字节是CLR头部,后续是UTF-16编码的字节序列:61 00 62 00 92 21 63 00 64 00(对应a、b、→、c、d的UTF-16小端编码)。
  • 找到"ab→cd"u8对应的静态字节数组,内存内容为61 62 E2 86 92 63 64(UTF-8编码),而ReadOnlySpan<byte>的内存仅为指向该数组的指针+长度值,占16字节栈空间。

内容的提问来源于stack exchange,提问作者FluidMechanics Potential Flows

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 22:07:38