发布日期:2008.8.27 作者:Anytao 
© 2008 Anytao.com ,Anytao原创作品,转贴请注明作者和出处。

说在,开篇之前

走钢丝的人,在刺激中体验快感。带着问题思考,在问题上迸发火花。

或者给问题以答案,或者给答案以问题,你可能永远无法看清全部,但是总能从一点突破很多。事实的关键就在于面对问题,我该如何思考?

String Interning(字符串驻留)就是这样一个值得思考的话题,带着问题思考,我们至少要理清以下几个问题:

  • 什么是string?
  • 什么是字符串驻留?
  • 字符串驻留的运行机制及执行过程?
  • 字符串驻留的其他问题?

带着几个问号,你必须知道的.NET,继续更多体验。

www.anytao.com

1 带着问题?

带着问题思考,是技术探索的最佳实践, 每当我收到很多朋友来函探讨技术的问题,总能给我很多的技术思索和惊喜,今天我们的话题就是由一个朋友的来函开始的,你可以通过链接打开KiMoGiGi在To 王涛 的问题一文中精彩绝伦的思考和探讨,带着他的提问,引着我的思考,完成本文对string的一点点探讨。

快捷参考
  • KiMoGiGi,《To 王涛 的问题》

www.anytao.com

首先,本文也无一例外的从8个测试开始,也希望读者能沿着这几个简单的示例来思考答案。如果对此包含热情,不妨可以试试,你开始了吗?

        // Release : code01, 2008/08/20        
        // Author  : Anytao, http://www.anytao.com 
        static void Main() 
        { 
            string s1 = "abc"; 
            Console.WriteLine(string.IsInterned(s1) ?? "null"); 
        }

这是个简单的例题,可以很快给出答案。

        // Release : code02, 2008/08/20        
        // Author  : Anytao, http://www.anytao.com
        static void Main() 
        { 
            string s1 = "ab"; 
            s1 += "c"; 
            Console.WriteLine(string.IsInterned(s1) ?? "null"); 
        }

稍加修改,这回的答案又该如何分析,我们继续。

        // Release : code03, 2008/08/20        
        // Author  : Anytao, http://www.anytao.com
        static void Main() 
        { 
            string s1 = "abc"; 
            string s2 = "ab"; 
            s2 += "c"; 
 
            string s3 = "ab"; 
            
            Console.WriteLine(string.IsInterned(s1) ?? "null"); 
            Console.WriteLine(string.IsInterned(s2) ?? "null"); 
 
            Console.WriteLine(string.IsInterned(s3) ?? "null"); 
        }

如果上述执行过程,你能很快给出答案,那么恭喜了,第一关看来不是那么费劲,我们接着思考,继续第二关:

        // Release : code04, 2008/08/20        
        // Author  : Anytao, http://www.anytao.com
        static void Main() 
        { 
            string s1 = "abc"; 
            string s2 = "ab"; 
            string s3 = s2 + "c"; 
            
            Console.WriteLine(string.IsInterned(s3) ?? "null"); 
        }

还有一个,我们继续

        // Release : code05, 2008/08/20        
        // Author  : Anytao, http://www.anytao.com
        static void Main() 
        { 
            string s2 = "ab"; 
            s2 += "c"; 
            
            Console.WriteLine(string.IsInterned(s2) ?? "null"); 
            
            string s1 = "abc"; 
        }

你的答案怎么是?我们还是接着迎接挑战:

        // Release : code06, 2008/08/20        
        // Author  : Anytao, http://www.anytao.com
        static void Main() 
        { 
            string s2 = "ab"; 
            s2 += "c"; 
            
            Console.WriteLine(string.IsInterned(s2) ?? "null"); 
            
            string s1 = GetStr(); 
        }         
        
        private static string GetStr() 
        { 
            return "abc"; 
        }

这是第二关了,你的思考肯定还在继续,我们第三关也呼之欲出:

        // Release : code07, 2008/08/20        
        // Author  : Anytao, http://www.anytao.com
        public const string s1 = "abc"; 
        
        static void Main() 
        { 
            string s2 = "ab"; 
            s2 += "c"; 
            
            Console.WriteLine(string.IsInterned(s2) ?? "null"); 
        }

最后一个,冲出藩篱:

        // Release : code08, 2008/08/20        
        // Author  : Anytao, http://www.anytao.com
        public static string s1 = "abc"; 
        
        static void Main() 
        { 
            string s2 = "ab"; 
            s2 += "c"; 
            
            Console.WriteLine(string.IsInterned(s2) ?? "null"); 
        }

过关斩将,三轮PK,是英雄比高。不管怎样,你的答案和思考,肯定会让大家对string刮目相看,是否和你一直以来的认识统一呢?在此感谢KiMoGiGi 给我的启示。有了问题,我们更需要的是思考、探讨和反思。

2 欲求思考

欲求思考,则从基本开始,对于理解整个string intern机制是大有裨益的,因此深入的第一步就从基本概念开始。随着我们分析的层层深入,就会发现看似曲折的结果,原来不过如此而已,这正是技术探求的最佳方式。

什么是string

什么是string呢,提起这个问题,我想下面的图例可以给出一点启示:

string在本质上就是一连串的有顺序的字符集合。

简单的说,string就是char[],而在.NET中string头一回具有了类的概念,暗合了.NET一切皆为对象的大一统格局。回归本质,我们重新审视如此另类而多彩的string,你会不禁明白,string本质上就是一个16位Unicode字符数组。打开string的Disassemble代码,我们可直击其本质:

    [Serializable, ComVisible(true)]
    public sealed class String : IComparable, ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string>
    {
    }

结合string的定义,我们可以看出其基本的特性主要包括:

  • 引用类型,string本质上是引用类型,相关内容参考《你必须知道的.NET》 对值类型和引用类型的讨论。
  • 字符串恒等性。
  • 字符串驻留性,本文的研究重点。
  • 密封性,由sealed关键字可见,sealed特性为实现字符串恒等性和字符串驻留机制,提供了基础保证,具体的原因参见《你必须知道的.NET》 关于string的相关论述。

关于这些特性并非本文关注的热点,还有大量的命题值得我们关注,总结起来还可包括:

  • 字符串比较:以等价规则而非恒等规则进行比较。
  • 常用方法:Trim()、ToLow()、Replace()、Split()、PadRight()、SubString()和Join()
  • 格式化。
  • 转移字符。
  • StringBuilder,另一个重要的话题。
  • Encoding,编码。
  • Culture & Internationalization,语言文化。
  • overloads == ,==重载。

由此可知,string真是一个丰富而多彩的技术仓库,饱含了.NET技术中很多精髓与技巧,我们不可能在本文中尽述其然,更多的论述和分析可以参考以下信息:

快捷参考

关,你可以参考:

  • string的更多技术与规则,可以参考《你必须知道的.NET》 8.3节“如此特殊:大话String”
  • 等价规则和恒等规则,可以参考《你必须知道的.NET》 8.2节“规则而定:对象判等”

www.anytao.com

接下来,本文的主题闪亮登场。

什么是字符串驻留(String Interning)

回归经典,我们首先给出MSDN对于字符串驻留的一点讨论:

公共语言运行库通过维护一个表来存放字符串,该表称为拘留池,它包含程序中以编程方式声明或创建的每个唯一的字符串的一个引用。因此,具有特定值的字符串的实例在系统中只有一个。

例如,如果将同一字符串分配给几个变量,运行库就会从拘留池中检索对该字符串的相同引用,并将它分配给各个变量。

之所以,将string这个熟悉的命题拿出来造轮子,并不是再造个轮子自己陶醉。关于string的轮子,实在太多了,而且个个不顺眼,它就像编程的精灵,四处可见随处都有。string是如此的重要,以至于CLR必须以特殊的方式来实现对string类型的管理、存取和布局,在这些复杂的特殊表象中,字符串驻留机制是string特殊性的集中体现,它的基本原理可以概括为:

  • CLR维护一个类似于哈希表的内部结构,用于维护对于字符串的统一管理。
  • 但JIT编译时,CLR首先查找哈希表,如果没有找到匹配的字符串记录,则在托管堆中创建新的string实例,并为哈希表添加一个键值对记录;下一次查找相同string时,则只返回该记录的值给第二次创建的string对象。
  • 通过这种方式,字符串驻留机制有效实现了对string的池管理,节省了大量的内存空间。

详细的字符串驻留机制,敬请参考:

快捷参考

关于字符串驻留机制的详细过程,不是本文所要解决的主要问题,你可以参考:

  • 《你必须知道的.NET》 8.3节“如此特殊:大话String” 中的详细分析
  • Artech, [原创]再说String

www.anytao.com

我们可以从code01尽情领略字符串驻留机制的基本原理,然而关于字符串驻留,并不是几句简单原理就能全面概括的问题。

注意:动态创建的字符串是不执行字符串驻留机制的,例如通过:

        // Release : code09, 2008/08/25                
        // Author  : Anytao, http://www.anytao.com
        static void Main()
        {
            string s1 = "abc";
            string s2 = "ab";
            string s3 = s2 + "c";
 
            Console.WriteLine(ReferenceEquals(s1, s3));
        }

但是对于“动态”二字的把握并非一件简单的事情,什么情况下执行字符串驻留,而什么时候不会执行字符串驻留,在.NET spec中我并没有找出足够精确的正解来阐释这个问题,例如code06示例中,string s1 = GetStr(); 的位置很大程度上决定了是否执行字符串驻留条件,从code06示例的结果可见,string.IsInterned(s2)并未收获返回“abc”的预期结果,而下面的示例则又给出意想不到的答案:

        // Release : code10, 2008/08/25                
        // Author  : Anytao, http://www.anytao.com
        static void Main()
        {
            //在这个位置返回abc
            string s1 = GetStr();
 
            string s2 = "ab";
            s2 += "c";
            Console.WriteLine(string.IsInterned(s2) ?? "null");
        }
 
        private static string GetStr()
        {
            return "abc";
        }

对比code06和code10,我们发现不同的只是string s1 = GetStr(); 的位置,而结果却大相径庭。

为什么?

位置的不同而导致触发字符串驻留机制是否执行的条件不同,这正是我们通过实例反向验证的最佳体现。那么,你的思考呢?

这些看似熟悉的问题,其实都值得推敲,本文没有太多的精力兼顾所有,只能在边缘之余探讨一下遗留在字符串驻留机制中一些并不是很清楚的问题,算是对字符串驻留机制的进一步探讨,主要包括:

  • CLR的加载过程。
  • intern pool在什么时候创建,如何创建?
  • 驻留机制的简述。
  • 方法的调用过程。
  • 介绍IsInterned和Intern方法。
  • string intern的失效和弊端。

以解决本文开题的几个典型的问题,同时顺便解答KiMoGiGi在To 王涛 的问题中提出的问题。下面我们一一揭开这些问题的神秘面纱。

作为字符串驻留机制探讨的第一篇,我们从问题出发引出对于字符串驻留的定义和概念,在未来的篇章中除了说明上述问题之外,我们还将力图解释开篇8个示例的个中结果,并对可能的情况和问题进行一些对比性的推敲。

事实上,由string intern而引发的技术论题,还有很多值得我们品味和玩味,这也正是这个本文及其后续篇章力图做出的努力。

敬请期待,本篇后文。。。

Anytao | 2008-08-27 | 你必须知道的.NET

http://www.anytao.com/  | Blog: http://anytao.cnblogs.com/ | Anytao原创作品,转贴请注明作者和出处,留此信息。

特别鸣谢

Jeffery Richter,对于我的问题,Jeffery先生及时给出自己的见解,让我顿时感受到大师的品格。

KiMoGiGi,是他的问题带来本文的思考,这些难得的线索构成了我们进行探讨的基础话题,巧妇难为无米之炊,因此需要特别感谢。

参考文献

(Book)Martin Fowler,Refactoring: Improving the Design of Existing Code

(cnblog)http://www.cnblogs.com/flier/archive/2004/07/08/22307.html

(cnblog)http://www.cnblogs.com/artech/archive/2007/05/31/765773.html

[你必须知道的.NET]第二十二回:字符串驻留(上)---带着问题思考相关推荐

  1. [你必须知道的.NET]第二十四回:认识元数据和IL(上)

    说在,开篇之前 很早就有说说Metadata(元数据)和IL(中间语言)的想法了,一直在这篇开始才算脚踏实地的对这两个阶级兄弟投去些细关怀,虽然来得没有<第一回:恩怨情仇:is和as>那么 ...

  2. [你必须知道的.NET]第二十五回:认识元数据和IL(中)

    说在,开篇之前 书接上回[第二十四回:认识元数据和IL(上)],我们对PE文件.程序集.托管模块,这些概念与元数据.IL的关系进行了必要的铺垫,同时顺便熟悉了以ILDASM工具进行反编译的基本方法认知 ...

  3. [你必须知道的.NET]第二十六回:认识元数据和IL(下)

    说在,开篇之前 书接上回:  第二十四回:认识元数据和IL(上), 第二十五回:认识元数据和IL(中) 我们继续. 终于到了,说说元数据和IL在JIT编译时的角色了,虽然两个回合的铺垫未免铺张,但是却 ...

  4. [你必须知道的.NET]第二十九回:.NET十年(上)

    引言 语言是程序开发者行走江湖的手上利器,各大门派的高手在论坛.博客为了自家门派争吵不已早是技术世界中的亮丽风景,虽多少为刚刚踏入江湖的新手提供了思考的素材,但也同时迷惑了初出茅庐的前行方向. 本文不 ...

  5. [你必须知道的.NET]第二十八回:说说Name这回事儿

    1 缘起 老赵在谈表达式树的缓存(2):由表达式树生成字符串中提到,在描述Type信息时讨论FullName或者AssemblyQualifiedName提供完整的Type信息,虽是小话题,但却是值得 ...

  6. [你必须知道的.NET]第二十回:学习方法论

    本文,源自我回答刚毕业朋友关于.NET学习疑惑的回复邮件. 本文,其实早计划在<你必须知道的.NET>写作之初的后记部分,但是因为个中原因未能如愿,算是补上本书的遗憾之一. 本文,作为[& ...

  7. [你必须知道的.NET]第十二回:参数之惑---传递的艺术(下)

    本文将介绍以下内容: 按值传递与按引用传递深论 ref和out比较 参数应用浅析 接上篇继续,『第十一回:参数之惑---传递的艺术(上)』 4.2 引用类型参数的按值传递 当传递的参数为引用类型时,传 ...

  8. [你必须知道的.NET]第二十一回:认识全面的null

    <你必须知道的.NET>网站 | Anytao技术博客  [你必须知道的.NET]第二十一回:认识全面的null 发布日期:2008.7.31 作者:Anytao © 2008 Anyta ...

  9. [你必须知道的.NET]第二十七回:interface到底继承于object吗?

    <你必须知道的.NET>网站 | Anytao技术博客  [你必须知道的.NET]第二十七回:interface到底继承于object吗? 发布日期:2009.03.05 作者:Anyta ...

最新文章

  1. WSAGetLastError()部分常见返回值
  2. 关于模型复杂度的一个想法
  3. JQuery之ajax异步请求Django后端
  4. mxnet基础到提高(53)-批量标准化(2)
  5. 怎么在linux指定目录下查找文件夹下,Linux下如何使用find命令指定目录查找文件...
  6. boost的chrono模块等待按键的测试程序
  7. java弃用标签_Java 9 揭秘(15. 增强的弃用注解)
  8. HDU-5023 线段树染色问题+延时标记
  9. LeetCode 1201. 丑数 III(最小公倍数+二分查找)
  10. 汇编指令速查手册(转)
  11. Android studio xpose的使用
  12. AiChallenger比赛记录之样本不均衡
  13. mysql sql语法解析器_Druid SQL 解析器概览
  14. 无法将数据库从SINGLE_USER模式切换回MULTI_USER模式(Error 5064),及查找SQL Server数据库中用户spid(非SQL Server系统spid)的方法...
  15. 堆溢出off-by-one(asis-ctf-2016 pwn 之 b00ks)
  16. 线性齐次方程组的通解 MATLAB
  17. qq空间java版_Java版 QQ空间自动登录无需拷贝cookie一天抓取30WQQ说说数据流程分析【转】...
  18. linux firefox体验,Firefox插件 让你在桌面浏览器体验Firefox OS(附安装教程)
  19. wma格式怎么转换mp3
  20. 整数二分详解---yxc

热门文章

  1. 基础 PHP 数据类型
  2. 小程序中获取高度以及设备的方法
  3. Java NIO系列教程(九) ServerSocketChannel
  4. 轻松精通数据库管理之道——运维巡检系列
  5. 《自己动手写Docker》书摘之三: Linux UnionFS
  6. mysql查看存储过程工作记录20130711
  7. 使用ArcGIS API for Silverlight实现地形坡度在线分析
  8. php之static静态变量详解
  9. shllter自动和手动实例
  10. 观察者模式定义了一种一对多的对象关系,当一个对象改变时,此对象对应的监控对象跟着改变!!!...