[你必须知道的.NET]第二十二回:字符串驻留(上)---带着问题思考
发布日期:2008.8.27 作者:Anytao
© 2008 Anytao.com ,Anytao原创作品,转贴请注明作者和出处。
说在,开篇之前 |
走钢丝的人,在刺激中体验快感。带着问题思考,在问题上迸发火花。 或者给问题以答案,或者给答案以问题,你可能永远无法看清全部,但是总能从一点突破很多。事实的关键就在于面对问题,我该如何思考? String Interning(字符串驻留)就是这样一个值得思考的话题,带着问题思考,我们至少要理清以下几个问题:
带着几个问号,你必须知道的.NET,继续更多体验。 |
1 带着问题?
带着问题思考,是技术探索的最佳实践, 每当我收到很多朋友来函探讨技术的问题,总能给我很多的技术思索和惊喜,今天我们的话题就是由一个朋友的来函开始的,你可以通过链接打开KiMoGiGi在To 王涛 的问题一文中精彩绝伦的思考和探讨,带着他的提问,引着我的思考,完成本文对string的一点点探讨。
快捷参考 |
首先,本文也无一例外的从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 Interning)
回归经典,我们首先给出MSDN对于字符串驻留的一点讨论:
公共语言运行库通过维护一个表来存放字符串,该表称为拘留池,它包含程序中以编程方式声明或创建的每个唯一的字符串的一个引用。因此,具有特定值的字符串的实例在系统中只有一个。
例如,如果将同一字符串分配给几个变量,运行库就会从拘留池中检索对该字符串的相同引用,并将它分配给各个变量。
之所以,将string这个熟悉的命题拿出来造轮子,并不是再造个轮子自己陶醉。关于string的轮子,实在太多了,而且个个不顺眼,它就像编程的精灵,四处可见随处都有。string是如此的重要,以至于CLR必须以特殊的方式来实现对string类型的管理、存取和布局,在这些复杂的特殊表象中,字符串驻留机制是string特殊性的集中体现,它的基本原理可以概括为:
- CLR维护一个类似于哈希表的内部结构,用于维护对于字符串的统一管理。
- 但JIT编译时,CLR首先查找哈希表,如果没有找到匹配的字符串记录,则在托管堆中创建新的string实例,并为哈希表添加一个键值对记录;下一次查找相同string时,则只返回该记录的值给第二次创建的string对象。
- 通过这种方式,字符串驻留机制有效实现了对string的池管理,节省了大量的内存空间。
详细的字符串驻留机制,敬请参考:
快捷参考 |
关于字符串驻留机制的详细过程,不是本文所要解决的主要问题,你可以参考:
|
我们可以从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