昨天看群里在讨论C#中的string驻留池,炒的火热,几轮下来理论一堆堆,但是在证据提供上都比较尴尬。虽然这东西很基础,但比较好的回答也不是那么容易,这篇我就以我能力范围之内跟大家分享一下

一:无处不在的池

开发这么多年,相信大家对‘池’ 这个概念都耳熟能详了,连接池,线程池,对象池,还有这里的驻留池,池的存在就是为了复用为了共享,独乐乐不如众乐乐,毕竟一个字符串的生成和销毁既浪费空间又浪费时间,还不如先养着。

1. 说说现象

通常我们臆想中是这么认为的,定义几个字符串变量,堆上就会分配几个string对象,其实这底层有一种叫驻留池技术可以做到如果两个字符串内容相同,那就在堆上只分配一个string对象,然后将引用地址分配给两个字符串变量,这样就可以大大降低了内存使用,如果用代码表示就是下面这样。

        public static void Main(string[] args){var str1 = "nihao";var str2 = "nihao";var b = string.ReferenceEquals(str1, str2);Console.WriteLine(b);}----------- output -----------
True

2. 实现原理

那怎么做到的呢? 其实CLR在运行时调用JIT把你的MSIL代码转成机器代码的时候会发现你的元数据中定义了相同内容的字符串对象,CLR就会把你的字符串放入它私有的的内部字典中,其中key就是字符串内容,value就是分配在堆上的字符串引用地址,这个字典就是所谓的驻留池,如果不是很明白,我来画一张图。

3. windbg验证

可以用windbg看一下栈中的str1和str2是否都指向了堆上对象的地址。

~0s -> !clrstack -l 在主线程的线程栈上找到变量str1和str2

0:000> ~0s
ntdll!ZwReadFile+0x14:
00007ff8`fea4aa64 c3              ret
0:000> !clrstack -l
OS Thread Id: 0x1c1c (0)Child SP               IP Call Site000000ac0b7fed00 00007ff889e608e9 *** WARNING: Unable to verify checksum for ConsoleApp2.exe
ConsoleApp2.Program.Main(System.String[]) [C:dreamCsharpConsoleApp1ConsoleApp2Program.cs @ 30]LOCALS:0x000000ac0b7fed38 = 0x0000024a21f22d480x000000ac0b7fed30 = 0x0000024a21f22d48000000ac0b7fef48 00007ff8e9396c93 [GCFrame: 000000ac0b7fef48]

从上面代码的 LOCALS 的 0x000000ac0b7fed38 = 0x0000024a21f22d480x000000ac0b7fed30 = 0x0000024a21f22d48可以看到两个局部变量的引用地址都是 0x0000024a21f22d48,说明指向的都是一个堆对象,接下来再把堆上的内容打出来。

0:000> !do 0x0000024a21f22d48
Name:        System.String
MethodTable: 00007ff8e7a959c0
EEClass:     00007ff8e7a72ec0
Size:        36(0x24) bytes
File:        C:WINDOWSMicrosoft.NetassemblyGAC_64mscorlibv4.0_4.0.0.0__b77a5c561934e089mscorlib.dll
String:      nihao
Fields:MT    Field   Offset                 Type VT     Attr            Value Name
00007ff8e7a985a0  4000281        8         System.Int32  1 instance                5 m_stringLength
00007ff8e7a96838  4000282        c          System.Char  1 instance               6e m_firstChar
00007ff8e7a959c0  4000286       d8        System.String  0   shared           static Empty>> Domain:Value  0000024a203d41c0:NotInit  <<

可以看到,果然是System.String对象,这就和我的图是相符的。

二 驻留池的验证

1. String下的驻留池验证方法

很遗憾的是水平有限,由于驻留池既不在堆中也不在栈上,目前还不知道怎么用windbg去打印CLR中驻留池字典内容,不过也可以通过 string.Intern 去验证。

        //// Summary://     Retrieves the system's reference to the specified System.String.//// Parameters://   str://     A string to search for in the intern pool.//// Returns://     The system's reference to str, if it is interned; otherwise, a new reference//     to a string with the value of str.//// Exceptions://   T:System.ArgumentNullException://     str is null.[SecuritySafeCritical]public static String Intern(String str);

从注释中可以看到,这个方法的意思就是:如果你定义的str在驻留池中存在,那么就返回驻留池中命中内容的堆上引用地址,如果不存在,将新字符串插入驻留池中再返回堆上引用,先上一下代码:

        public static void Main(string[] args){var str1 = "nihao";var str2 = "nihao";//验证nihao是否在驻留池中,如果存在那么str3 和 str1,str2一样的引用var str3 = string.Intern("nihao");//验证新的字符串内容是否进入驻留池中var str4 = string.Intern("cnblogs");var str5 = string.Intern("cnblogs");Console.ReadLine();}

接下来分别验证一下str3是否也是和str1和str2一样的引用,以及str5是否存在驻留池中。

ConsoleApp2.Program.Main(System.String[]) [C:dreamCsharpConsoleApp1ConsoleApp2Program.cs @ 37]LOCALS:0x00000047105fea58 = 0x0000018537312d480x00000047105fea50 = 0x0000018537312d480x00000047105fea48 = 0x0000018537312d480x00000047105fea40 = 0x0000018537312d700x00000047105fea38 = 0x0000018537312d70

从五个变量地址中可以看到,nihao已经被str1,str2,str3共享,cnblogs也进入了驻留池中实现了共享。

2. 运行期相同string是否进入驻留池

这里面有一个坑,前面讨论的相同字符串都是在编译期就知道的,但运行时中的相同字符串是否也会进入驻留池呢? 这是一个让人充满好奇的话题,可以试一下,在程序运行时接受IO输入内容hello,看看是否和str1,str2共享引用地址。

         public static void Main(string[] args){var str1 = "nihao";var str2 = "nihao";var str3 = Console.ReadLine();Console.WriteLine("输入完成!");Console.ReadLine();}0:000> !clrstack -l
000000f6d35fee50 00007ff889e7090d *** WARNING: Unable to verify checksum for ConsoleApp2.exe
ConsoleApp2.Program.Main(System.String[]) [C:dreamCsharpConsoleApp1ConsoleApp2Program.cs @ 33]LOCALS:0x000000f6d35fee98 = 0x000002cb1a552d480x000000f6d35fee90 = 0x000002cb1a552d480x000000f6d35fee88 = 0x000002cb1a555f28
0:000> !do 0x000002cb1a555f28
Name:        System.String
MethodTable: 00007ff8e7a959c0
EEClass:     00007ff8e7a72ec0
Size:        36(0x24) bytes
File:        C:WINDOWSMicrosoft.NetassemblyGAC_64mscorlibv4.0_4.0.0.0__b77a5c561934e089mscorlib.dll
String:      nihao
Fields:MT    Field   Offset                 Type VT     Attr            Value Name
00007ff8e7a985a0  4000281        8         System.Int32  1 instance                5 m_stringLength
00007ff8e7a96838  4000282        c          System.Char  1 instance               6e m_firstChar
00007ff8e7a959c0  4000286       d8        System.String  0   shared           static Empty>> Domain:Value  000002cb18ad39f0:NotInit  <<

从上面内容可以看到,从Console.ReadLine接收到的引用地址是 0x000002cb1a555f28 ,虽然是相同内容,但却没有使用驻留池,这是因为驻留池在JIT静态解析期就已经解析完成了,也就无法享受复用之优,如果还想复用的话,在 Console.ReadLine() 包一层string.Intern即可,如下所示:

        public static void Main(string[] args){var str1 = "nihao";var str2 = "nihao";var str3 = string.Intern(Console.ReadLine());Console.WriteLine("输入完成!");Console.ReadLine();}ConsoleApp2.Program.Main(System.String[]) [C:dreamCsharpConsoleApp1ConsoleApp2Program.cs @ 33]LOCALS:0x0000008fac1fe9c8 = 0x000001ff46582d480x0000008fac1fe9c0 = 0x000001ff46582d480x0000008fac1fe9b8 = 0x000001ff46582d48

可以看到这个时候str1,str2,str3共享一个内存地址 0x000001ff46582d48

四: 总结

驻留池技术是个很 的东西,很好的解决字符串在堆上的重复分配问题,大大减小了堆的内存占用,但也要明白运行期的IO输入无法共享驻留池的解决方案。

好了,本篇就说到这里,希望对你有帮助!

string转成对象_非常简单的string驻留池,你对它真的了解吗相关推荐

  1. string转成对象_详解Java I/O流(五),对象序列化

    对象序列化 什么是序列化和反序列化呢? 序列化就是将对象转成字节序列的过程,反序列化就是将字节序列重组成对象的过程. 在这里插入图片描述 为什么要有对象序列化机制 程序中的对象,其实是存在有内存中,当 ...

  2. java string改变的影响_为什么Java的string类要设成immutable(不可变的)

    最流行的Java面试题之一就是:什么是不可变对象(immutable object),不可变对象有什么好处,在什么情况下应该用,或者更具体一些,Java的String类为什么要设成immutable类 ...

  3. string转成对象_JAVA学习分享[008]——面向对象深化(一)

    1.抽象类和抽象方法 使用abstract修饰符修饰的方法被称为抽象方法 抽象方法不能有方法体,子类必须重写抽象类中所有的抽象方法 抽象类中不一定包含抽象方法,但包含抽象方法的类一定是抽象类 抽象类不 ...

  4. java如果把字符串转成对象_为什么Java中的字符串对象是不可变的,有什么好处?...

    专注于Java领域优质技术号,欢迎关注 原创: 阿杜的世界 阅读本文大概需要 4分钟. 所谓不可变对象,是指一个对象在创建后,它的内部状态不会被改变的对象.这意味着当我们将一个不可变对象的引用赋值给某 ...

  5. java有string这个类型吗_关于java的String类型

    String类型的不可变 众所周知,String类型是不可变的,一旦String对象被创建好了则这个字符串中的序列就不可改变. 为什么String类型是不可变? 根据阅读源码可知,String类是一个 ...

  6. ruby json转化成对象_系统架构迁移实例:从Ruby到Rust

    前几年随着RoR(Ruby on Rails)框架的流行,很多初创企业选择Ruby和Rails作为基础开发框架,构建和快速迭代了其业务系统.但是随着业务规模的不断壮大,性能问题成了一个突出的问题,所以 ...

  7. 对象tostring后怎么转成对象_给对象发语音学猫叫后,对象说晚上要弄哭我.........

    最近网上发起了一项挑战: 给对象或者朋友发语音学猫叫, 问问他/她们萌不萌, 他/她们会是什么反应? 结果网友们的反馈截图 可以说是非常爆笑了 !!! 除了网友们分享的截图外,很多猫来了粉丝也参加了这 ...

  8. 对象tostring后怎么转成对象_和女生相亲后怎么联系对方?和相亲对象该如何聊天...

    马上就要过年了,很多男青年又会被家里催着相亲,无论你正在相亲还是在相亲的路上,许多男生都有这样的困扰,就是相亲之后该怎么和对方联系?需要天天联系对方吗?该怎么跟相亲对象聊天呢? 下面小编就和大家分享和 ...

  9. 对象tostring后怎么转成对象_对象出轨了怎么办?对象出轨正常处理方式

    当你还在为着能够有一个美好的生活而努力拼搏的时候,当你还在憧憬着能够与你的爱人走入婚姻殿堂的时候,没想到他已经背叛了你,任何一个人,当他听到这个消息的时候,心情都会非常的沮丧,那么对象出轨了怎么办?肯 ...

最新文章

  1. Qt中两种定时器用法
  2. “头移植模型”论文称换头术可行 业内疑两大问题未解
  3. ORACLE中的包和包体
  4. NLP:对字符串按照一个、多个自定义分隔符进行分割、将列表转为字符串同时自定义连接符
  5. Cython入门.VS.C++
  6. mysql ondelete_MySQL on delete cascade语句
  7. spark学习-Spark的Core理解
  8. 高德地图画带箭头的线_模具装配图画成这样,那才真的叫标准!
  9. 为什么有的人洗脚时要在水里滴几滴醋?
  10. Java 单例模式详解(转)
  11. 蓝桥杯每日真题之货物摆放
  12. 手机版python3.8.1下载_python3.8.1汉化版
  13. (附源码)计算机毕业设计ssm房屋租赁管理系统
  14. VS注册登录不显示界面内容
  15. WARNING: The converted filename is an ASM fully qualified filename.
  16. AR/VR软件与硬件技术
  17. mysql常用存储引擎以及悲观锁与乐观锁
  18. 使用Python爬取招聘数据、数据处理与可视化
  19. 令克软件再推OpenAPI与MAS系统服务,强大引擎赋能券商多元化发展
  20. 冯诺依曼结构、哈佛结构、改进型哈佛结构

热门文章

  1. 除系统分区外未找到其它非系统分区导致软件无法运行解决方案
  2. 计算机不能代替人类英语,英语作文 谈谈计算机1我们已进入了计算机时代2计算机有许多优点3计算机不能代替人类...
  3. Android:使用SharedPreferences进行数据存储
  4. 洛谷——P1317 低洼地
  5. SpringBoot:Unable to start LiveReload server(已解决)
  6. C++ TCP socket 非阻塞连接超时设定方式
  7. TypeScript学习(八):数组的补充及内置对象说明
  8. 'React' must be in scope when using JSX react/react-in-jsx-scope报错:
  9. jQuery.validator.addMethod 自定义验证方法
  10. JSCore的基本使用