.net 内存
1 了解String数据的内存分配方式
 编写一个控制台应用程序,输入以下测试代码:
    class Program
    {
        static void Main(string[]  args)
        {
            String s = "a";
            s =  "abcd";
        }
    }
使用.NET Framework 2.0 SDK提供的ildasm.exe工具查看生成的MSIL指令:
01  .method private hidebysig static void  Main(string[] args) cil  managed
02  {
03    .entrypoint
04    // 代码大小       14 (0xe)
05     .maxstack  1
06    .locals init ([0] string s)
07    IL_0000:   nop
08    IL_0001:  ldstr      "a"
09    IL_0006:  stloc.0
10     IL_0007:  ldstr      "abcd"
11    IL_000c:  stloc.0
12    IL_000d:   ret
13  } // end of method Program::Main
 简要解释一下上述MSIL指令代码:
 第06句给局部变量s分配一个索引号(索引号从0开始
 在编译时编译器会将代码中的两个字串"a"和"abcd
 第08句使用ldstr指令为字串对象"a"分配内存
 第09句使用stloc指令从线程堆栈顶弹出先前压入的对象引用
 同样的过程对"abcd"重复进行一次,所以这两句简单的代码
            String s = "a";
            s = "abcd";
 将会导致CLR使用ldstr指令分配两次内存。
 根据上述分析,读者一定明白了String变量的内容是只读的
 下面对代码中常见的字串用法进行分析,从中读者可以知道如何避免
2 尽量少使用字串加法运算符
请看以下两段代码:
  (1)          String s1 = "ab";
                     s1+="cd";
   (2)          String s1="ab"+"cd";
 这两段代码运行结果一样,但速度一样快吗?
 请看第(1)段代码生成的MSIL指令:
  .locals init ([0] string s1)
  IL_0000:  nop
  IL_0001:  ldstr       "ab"
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldstr       "cd"
  IL_000d:  call       string  [mscorlib]System.String:
                              
  IL_0012:  stloc.0
  IL_0013:  ret
再看第(2)段代码生成的指令:
 .locals init ([0] string s1)
  IL_0000:  nop
  IL_0001:  ldstr       "abcd"
  IL_0006:  stloc.0
  IL_0007:  ret
 可以很清楚地看到,第(1)段代码将导致String类的Con
3 避免使用加法运算符连接不同类型的数据
 
请看以下代码:
 String str = "100+100=" + 200;
     Console.Writeline(str);
生成的MSIL指令为:
  .maxstack  2
  .locals init ([0] string str)
  IL_0000:  nop
   IL_0001:  ldstr      "100+100="
  IL_0006:  ldc.i4     0xc8
   IL_000b:  box        [mscorlib]System.Int32
  IL_0010:  call       string  [mscorlib]System.String:
                              
  IL_0016:  ldloc.0
  IL_0017:   call       void [mscorlib]System.Console:
  IL_001c:   nop
  IL_001d:  ret
 可以清晰地看到,这两句C#代码不仅导致了String类的Co
 Concat()方法会导致CLR为新字串分配内存空间
 改为以下代码:
            String str = "100+100=";
             Console.Write(str);
            Console.WriteLine(200);
生成的MSIL指令为:
  .maxstack  1
  .locals init ([0] string str)
  IL_0000:  nop
   IL_0001:  ldstr      "100+100="
  IL_0006:  stloc.0
  IL_0007:   ldloc.0
  IL_0008:  call       void  [mscorlib]System.Console:
  IL_000d:  nop
  IL_000e:   ldc.i4     0xc8
  IL_0013:  call       void  [mscorlib]System.Console:
  IL_0018:  nop
  IL_0019:   ret
可以看到,虽然多了一次方法调用(Console.Write)方法,但却避免了复杂的装箱操作,也避免了调用String.Concat()方法对内存的频繁分配操作,性能更好。
4.在循环中使用StringBuilder代替String实现
 在某些场合需要动态地将多个子串连接成一个大字串
            String str ="";
            for (int i = 1; i <= 10;  i++)
            {
                str += i;
                 if(i<10)
                    str += "+";
            }
 上述代码将生成一个字串:1+2+…+10。
 有了前面的知识,读者一定知道这将导致进行10次装箱操作
 改为以下代码,程序性能会好很多:
           //预先分配1K的内存空间
            StringBuilder sb = new  StringBuilder(1024);
            for (int i = 1; i <= 10;  i++)
            {
                sb.Append(i);
                 if(i<10)
                    sb.Append("+");
             }
            String result = sb.ToString();
 通过使用ildasm.exe工具查看生成的MSIL代码,发现虽然上述代码生成的MSIL指令比前面多了7条
 
20:47:34 | 固定链接 | 23 |
 正在加载... | 
2007年1月1日星期一
.NET值类型变量"活"在哪个堆栈中?
不管是什么语言编的.NET程序,最后都会被各自的编译器编译成MSIL。当程序运行时,.NET JIT编译器从程序集中读入IL指令并将其动态编译为可被本地CP
 
正在加载...
0 Comments:
Post a Comment
<< Home