公司的系统架构师问起了我String的值类型特性。说真的,我还差不多忘光了。
趁着周末,总结了一下之前的理解。同时也结合了栈与堆的特点来分析:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 //栈(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等
6 //堆(head)— C++中由程序员分配释放,C#使用托管机制,交给GC回收。
7 //值类型 在内存中的直接存储在栈中,而引用类型,如String str=new String("char"),new出的实例在堆中存放,并把存储地址引用给栈区中的str对象。
8 //值类型,在赋值的时候把自己的副本附给了另外的变量
9 //引用类型,在赋值的时候把堆的地址复制给另外的变量
10 int i = 2;
11 int j = i;
12 i = 5;
13 Console.WriteLine("i:{0} j:{1}",i,j);
14 String hello = "Hello";
15 String test = hello;
16 hello = "Hi";
17 Console.WriteLine("hello:{0} test:{1} ",hello,test);
18 //在这个测试中,说明了String类型有值类型的特征。
19 //在堆中重新分一块内存空间给它放新的值而旧的依然存在,而不是覆盖原先的值是.原先的值将等着CG来回收。
20 //正因为如果大量的使用String 有可能影响到整个系统性能。
21 //那么栈直接由编译器处理,不需要GC 为什么不直接用栈 而还需要堆呢?
22 //原因在于栈是预留空间,堆是动态分配。
23 //如果你用 int [] i=int[1000],而实际上只用了其中的几个,那么不也是浪费吗!
24 }
25 }
3 static void Main(string[] args)
4 {
5 //栈(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等
6 //堆(head)— C++中由程序员分配释放,C#使用托管机制,交给GC回收。
7 //值类型 在内存中的直接存储在栈中,而引用类型,如String str=new String("char"),new出的实例在堆中存放,并把存储地址引用给栈区中的str对象。
8 //值类型,在赋值的时候把自己的副本附给了另外的变量
9 //引用类型,在赋值的时候把堆的地址复制给另外的变量
10 int i = 2;
11 int j = i;
12 i = 5;
13 Console.WriteLine("i:{0} j:{1}",i,j);
14 String hello = "Hello";
15 String test = hello;
16 hello = "Hi";
17 Console.WriteLine("hello:{0} test:{1} ",hello,test);
18 //在这个测试中,说明了String类型有值类型的特征。
19 //在堆中重新分一块内存空间给它放新的值而旧的依然存在,而不是覆盖原先的值是.原先的值将等着CG来回收。
20 //正因为如果大量的使用String 有可能影响到整个系统性能。
21 //那么栈直接由编译器处理,不需要GC 为什么不直接用栈 而还需要堆呢?
22 //原因在于栈是预留空间,堆是动态分配。
23 //如果你用 int [] i=int[1000],而实际上只用了其中的几个,那么不也是浪费吗!
24 }
25 }
1 class StringAndStringBuilderDemo
2 {
3 static void Main(string[] args)
4 {
5 const int number=50000;
6 long Time=Environment.TickCount;
7 //String是不可变的,指它在堆上创建的内存空间里面的值不能被修改,而是通过创建新的空间。再返回该地址的引用。原来的空间在GC的时候回收。
8 String str = null;
9 for (int i = 0; i < number; i++)
10 {
11 str += i.ToString();
12 }
13 Console.WriteLine("String: {0} Mesl", Environment.TickCount - Time);
14
15 Time = Environment.TickCount;
16 //StringBuilder 在创建的时候在堆中生成一片内存空间,如果空间足够就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大
17 StringBuilder sb = new StringBuilder();
18 for (int i = 0; i < number; i++)
19 {
20 sb.Append(i);
21 }
22 Console.WriteLine("StringBuilder: {0} Msel", Environment.TickCount - Time);
23 string tmpStr1 = "A";
24 string tmpStr2 = tmpStr1;
25 Console.WriteLine(tmpStr1);
26 Console.WriteLine(tmpStr2);
27 tmpStr1 = "B";
28 Console.WriteLine(tmpStr1);
29 Console.WriteLine(tmpStr2);
30 //输出B A 证明 String 是在堆中创建了新的内存空间,并把其地址的引用赋予tmpStr1;
31 Console.ReadLine();
32 }
33 }
2 {
3 static void Main(string[] args)
4 {
5 const int number=50000;
6 long Time=Environment.TickCount;
7 //String是不可变的,指它在堆上创建的内存空间里面的值不能被修改,而是通过创建新的空间。再返回该地址的引用。原来的空间在GC的时候回收。
8 String str = null;
9 for (int i = 0; i < number; i++)
10 {
11 str += i.ToString();
12 }
13 Console.WriteLine("String: {0} Mesl", Environment.TickCount - Time);
14
15 Time = Environment.TickCount;
16 //StringBuilder 在创建的时候在堆中生成一片内存空间,如果空间足够就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大
17 StringBuilder sb = new StringBuilder();
18 for (int i = 0; i < number; i++)
19 {
20 sb.Append(i);
21 }
22 Console.WriteLine("StringBuilder: {0} Msel", Environment.TickCount - Time);
23 string tmpStr1 = "A";
24 string tmpStr2 = tmpStr1;
25 Console.WriteLine(tmpStr1);
26 Console.WriteLine(tmpStr2);
27 tmpStr1 = "B";
28 Console.WriteLine(tmpStr1);
29 Console.WriteLine(tmpStr2);
30 //输出B A 证明 String 是在堆中创建了新的内存空间,并把其地址的引用赋予tmpStr1;
31 Console.ReadLine();
32 }
33 }
输出为:
String: 16500 Mesl
StringBuilder: 31 Msel
A
A
B
A
A
A
B
A