.NET中的字符串在内存中的存储方式

 更新时间:2023年07月17日 09:01:57   作者:Artech  
这篇文章主要介绍了.NET的字符串在内存中是如何存储的,今天来讨论下这个问题,对.NET字符串内存存储相关知识感兴趣的朋友跟随小编一起看看吧

毫无疑问,字符串是我们使用频率最高的类型。但是如果我问大家一个问题:“一个字符串对象在内存中如何表示的?”,我相信绝大部分人回答不上来。我们今天就来讨论这个问题。

一、字符串对象的内存布局

从“值类型”和“引用类型”来划分,字符串自然属于引用类型的范畴,所以一个字符串对象自然采用引用类型的内存布局。我在很多文章中都介绍过引用类型实例的内存布局(《以纯二进制的形式在内存中绘制一个对象》 和《如何将一个实例的内存二进制内容读出来?》,总的来说整个内存布局分三块:ObjHeader + TypeHandle + Payload。对于一般的引用类型实例来说,最后一部分存放的就是该实例所有字段的值,但是字符串有点特别,它有哪些字段呢?

说到这里,可能有人想去反编译一下String类型,看看它定义了那些字段。其实没有必要,字符串这个类型有点特别,它的Payload部分由两部分组成:字符串长度(不是字节长度)+编码的文本,下图揭示了字符串对象的内存布局。那么具体采用怎样的编码方式呢?可能很多人会认为是UTF-8,实在不然,它采用的是UTF-16,大部分字符通过两个字节来表示,少数的则需要使用四个字节。至于字节序,自然是使用小端字节序。

二、以二进制的方式创建一个String对象

在《以纯二进制的形式在内存中绘制一个对象》中,我们通过构建一个字节数组来表示创建的对象,现在我们依然可以采用类似的方式来创建一个真正的String对象。如下所示的AsString方法用来将用于承载字符串实例的字节数组转换成一个String对象,至于这个字节数组的构建,则有CreateString方法完成。CreateString方法根据指定的字符串内容创建一个String对象,并利用输出参数返回该对象映射在内存中的字节数组。

static unsafe string CreateString(string value, out byte[] bytes)
{
    var byteCount = Encoding.Unicode.GetByteCount(value);
    // ObjHeader + TypeHandle + Length + Encoded string
    var size = sizeof(nint) + sizeof(nint) + sizeof(int) + byteCount;
    bytes = new byte[size];
    // TypeHandle
    BinaryPrimitives.WriteInt64LittleEndian(bytes.AsSpan(sizeof(nint)), typeof(string).TypeHandle.Value.ToInt64());
    // Length
    BinaryPrimitives.WriteInt32LittleEndian(bytes.AsSpan(sizeof(nint) * 2), value.Length);
    // Encoded string
    Encoding.Unicode.GetBytes(value).CopyTo(bytes, 20);
    return AsString(bytes);
}
static unsafe string AsString(byte[] bytes)
{
    string s = null!;
    Unsafe.Write(Unsafe.AsPointer(ref s), new IntPtr(Unsafe.AsPointer(ref bytes[8])));
    return s;
}

由于我们需要创建一个字节数组来表示String对象,所以必须先计算出这个字节数组的长度。我们在上面说过,String类型采用UTF-16/Unicode编码方式,所以我们调用Encoding.Unicode的GetByteCont方法可以计算出指定的字符串编码后的字节数。在此基础上我们还需要加上通过一个整数(sizeof(int))表示字符串长度和TypeHandle(sizeof(nint))和ObjHeader(sizeof(nint),含padding),就是整个String实例在内存中占用的字节数。

接下来我们填充String类型的TypeHandle的值(String类型方法表地址)、字符串长度和编码后的字节,最终将填充好的字节数组作为参数调用AsString方法,返回的就是我们创建的String对象。CreateString方法针字符串对象的创建可以通过如下的代码来验证。

var literal = "foobar";
string s = CreateString(literal, out var bytes);
Debug.Assert(literal == s);

对于上面定义的AsString方法来说,作为输入参数的字节数组字符串实例的内存片段,所以该方法针对同一个数组返回的都是同一个实例,如下的演示代码证明了这一点。

var literal = "foobar";
CreateString(literal, out var bytes);
var s1 = AsString(bytes);
var s2 = AsString(bytes);
Debug.Assert(ReferenceEquals(s1,s2));

三、字符串的“可变性”

我们都知道字符串一经创建就不会改变,但是对于上面创建的字符串来说,由于我们都将承载字符串实例的内存字节都拿捏住了,那还不是想怎么改就怎么改。比如在如下所示的代码片段中,我们将同一个字符串的文本从“foo”改成了“bar”。

var literal = "foo";
var s = CreateString(literal, out var bytes);
Debug.Assert(s == "foo");
Encoding.Unicode.GetBytes("bar").CopyTo(bytes, 20);
Debug.Assert(s == "bar");

到此这篇关于.NET的字符串在内存中是如何存储的吗?的文章就介绍到这了,更多相关.NET字符串内存存储内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • asp.net关于onpropertychange和oninput事件实现代码

    asp.net关于onpropertychange和oninput事件实现代码

    文本框,数据列表,当在文本框中输入条件时需要实时刷新数据列表,而且需要满足多浏览器(IE6.0/7.0/8.0,FireFox,Opera,google chrome,Safair)其功能类似google的智能匹配,我是用asp.net实现的。
    2009-11-11
  • 如何在网站级别动态更改主题

    如何在网站级别动态更改主题

    如何在网站级别动态更改主题...
    2007-04-04
  • DataView.RowFilter的使用(包括in,like等SQL中的操作符)

    DataView.RowFilter的使用(包括in,like等SQL中的操作符)

    这篇blog转自C# examples,对DataView.RowFilter做了详细介绍,能像SQL中使用in,like等操作符一样进行过滤查询,并附有实例,使用方便。
    2011-07-07
  • Asp.Net Core利用xUnit进行主机级别的网络集成测试详解

    Asp.Net Core利用xUnit进行主机级别的网络集成测试详解

    这篇文章主要给大家介绍了关于Asp.Net Core利用xUnit进行主机级别的网络集成测试的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们来一起看看吧
    2018-12-12
  • Attribute/特性心得随笔

    Attribute/特性心得随笔

    从事asp.net工作的相关人员对Attribute并不陌生吧,本文就来为大家介绍下Attribute特性,下面有个不错的示例,喜欢的朋友感受下
    2013-11-11
  • 防止重复提交 仅提交一次的终极绝杀技

    防止重复提交 仅提交一次的终极绝杀技

    防止重复提交,通用的思路,就是当用户点击提交按钮后,在浏览器中用JS将按钮disable掉,从而阻止用户继续点击该按钮,实现防止重复提交的目的。网上防止重复提交的文章已经不少了,为啥我还要写呢,显然我不是吃饱了撑的。。。
    2010-08-08
  • asp .net core静态文件资源的深入讲解

    asp .net core静态文件资源的深入讲解

    这篇文章主要给大家介绍了关于asp .net core静态文件资源的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • asp.net网站防恶意刷新的Cookies与Session解决方法

    asp.net网站防恶意刷新的Cookies与Session解决方法

    这篇文章主要介绍了asp.net网站防恶意刷新的Cookies与Session解决方法,分别以实例的形式讲述了采用cookie法与session法实现WEB页面防止恶意刷新的技巧,需要的朋友可以参考下
    2014-09-09
  • .NET使用报表工具FastReport实现打印功能

    .NET使用报表工具FastReport实现打印功能

    这篇文章介绍了.NET使用报表工具FastReport实现打印功能的方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-03-03
  • 一步步打造简单的MVC电商网站BooksStore(4)

    一步步打造简单的MVC电商网站BooksStore(4)

    这篇文章主要和大家一起一步步打造一个简单的MVC电商网站,MVC电商网站BooksStore第四篇,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04

最新评论