中国最专业的商业控件服务网

慧都控件网

电话:400-700-1020(免费)或023-66090381

深入浅出.NET字符串拘留池

作者:不详 出处:网络转载  2010/3/4 15:51:15 阅读 0 次

  本文将从.NET字符串拘留池讲起,希望大家在今后碰到这样的问题时,能正确处理。公共语言运行库通过维护一个表来存放字符串,该表称为拘留池,它包含程序中以编程方式声明或创建的每个唯一的字符串的一个引用.因此,具有特定值的字符串的实例在系统中只有一个。

  甘特图Xgantt(全球首款甘特图32年历史)   Visual WebGui (完美的用户界面解决方案)
  Stimulsoft Reports.Net (报表生成控件)   DXperience Uni Premium 白金版(慧都独家)

  XiaoMing在博客园上发表的《年前的面试经历(二) 》中回贴众多,其中谈到一个面试题:string a = "a" + "b"到底分配几次内存。

  这涉及到CLR内部的.NET字符串拘留池(string interning pool)问题。

  网友 Ivony和 横刀天笑的回贴引用资料指出拘留池是进程范围内的,因此,有可能以下定义字串常量的代码不会导致分配两次内存——因为另一个进程可能已经在拘留池中创建了“a”或 “ab”这两个字串对象。


string s1 = "a";   
string s2 = "a";   
string s3 = "a" + "b";   
Console.WriteLine(s1);   
Console.WriteLine(s2);   
Console.WriteLine(s3); 

  我对此结论有点怀疑,于是到Google和MSDN中查找,发现.NET字符串拘留池的有关资料很混乱。最后决定自己编程作实验。

  String类有一个IsInterned()方法用于检测一个字串是否在拘留池中,另一个Intern()方法用于将一个字串加入拘留池中。

  为此,我写了以下测试代码:


class Program   
    {   
        //static string outerstr = "j";     
          
        static void Main(string[] args)   
        {   
              
            string str = new string('j',1);  //动态构建的字串,不会放到拘留池中   
            if (string.IsInterned(str) == null)   
                Console.WriteLine(str + " is not interned");   //不在拘留池   
            else   
                Console.WriteLine(str+ " is interned"); //在拘留池   
 
            Console.ReadKey();   
            string s = string.Intern(str);  //强制加入拘留池   
          //再次检测   
           if (string.IsInterned(str) == null)   
                Console.WriteLine(str + "is not interned");   
            else   
                Console.WriteLine(str+ " is interned");   
            Console.ReadKey();              
        } 
 

       上述代码运行结果如下:


  j is not interned
      j is interned

  不管你运行多少次,也不管你是否同时运行多个此程序的实例,始终结果是一致的,都是上面的结果。这说明进程结束后,字串拘留池中的与此进程所装载的程序集相关的字串常量对象被清除。

  现在取消对outerstr 变量的注释,结果变为:


  j is interned 
      j is interned

  这说明程序集中的常量“j”在装载时被加入到了字串拘留池中,所以才有上述结果。

  还有一个问题,字串拘留池中的对象能否跨越不同进程边界共享?

  编写另一个测试程序:


class Program   
   {   
          
       static void Main(string[] args)   
       {   
 
           string str = new string('j', 1);   
           Console.WriteLine(string.IsInterned(str)==null);     
           Console.ReadKey();   
       }   
   } 

  不管前一个测试程序是否在运行,上述代码始终输出true,说明“j”这个字串没有在拘留池中,此进程无法获取另一个进程追加到字串拘留池中的“j”字串。

  从这些实验是否可以得出以下结论?

  当进程运行结束,此进程所加载的程序集中所定义的字串常量对象会被CLR从字串拘留池中移除。

  因此,CLR字串拘留池中的字串常量是“进程和程序集相关”的。

  应用程序所定义的字串常量对象在应用程序域装载程序集时被加入到字串拘留池中。

  所以,字串拘留池中的字串对象不能跨进程共享。不然,我们如何解释代码运行的结果?

  由于同一进程中可以创建多个应用程序域,我还没有编写代码测试字串拘留池中的字串对象是否可以在属于同一进程的多个应用程序域共享。此问题留待进一步探索。

  有无高人能彻底解释清楚这一问题?

  补充:

  我的测试环境是Windows 7 + Visual Studio 2010 RC.

慧都年终促销第2季

新闻搜索

Dxperience白金版满足你全面需求