C# 字符串驻留机制
目录
一、具有相同字符序列的String对象不会重复创建
二、字符串驻留机制同样于string literal + string literal的运算
三、字符串驻留机智不适合variable + string literal形式
四、调用string.Intern可以对运算结果进行强制驻留
五、驻留的字符串不能被GC回收
六、字符串驻留是基于整个进程的
1 public class StringInMemory
2 {
3 /// <summary>
4 /// 1. 具有相同字符序列的String对象不会重复创建
5 /// </summary>
6 public static void BuildString()
7 {
8 var str1 = "ABCDEFG";
9 var str2 = "ABCDEFG";
10 var str3 = "1234567";
11 var str4 = "1234567";
12
13 GC.Collect();
14 System.Diagnostics.Debug.Assert(string.ReferenceEquals(str1, str2));
15 }
16
17 /// <summary>
18 /// 2. 字符串驻留机制同样于string literal + string literal的运算
19 /// </summary>
20 public static void BuildString2()
21 {
22 var str1 = "ABCDEFG";
23 var str2 = "ABCD" + "EFG";
24 var str3 = "1234567";
25 var str4 = "1234" + "567";
26
27 System.Diagnostics.Debug.Assert(string.ReferenceEquals(str1, str2));
28 }
29
30 /// <summary>
31 /// 3. 字符串驻留机智不适合Variable + string literal形式
32 /// </summary>
33 public static void BuildString3()
34 {
35 ///无论是变量和一个字符串常量相加,还是两个字符串常量相加,运算的结果“ABCDEFG1234678”
36 ///并没有被驻留下来(实际上此时它已经是一个垃圾对象,GC可以对其进行回收)。
37 var str1 = "ABCDEFG";
38 var str2 = "1234567";
39 var str3 = "ABCDEFG" + str2;
40 var str4 = str1 + "1234567";
41 var str5 = str1 + str2;
42
43 System.Diagnostics.Debug.Assert(string.ReferenceEquals(str3, str4));
44 }
45
46 /// <summary>
47 /// 4. 调用string.Intern可以对运算结果进行强制驻留
48 /// </summary>
49 public static void BuildString4()
50 {
51 ///虽然涉及到变量的字符串连接运算结果不会被驻留,但是我们可以通过调用string.Intern方法对其进行强制驻留,
52 ///该方法会迫使传入传入参数表示的字符串被保存到驻留池中
53 var str1 = "ABCDEFG";
54 var str2 = "1234567";
55 var str3 = string.Intern("ABCDEFG" + str2);
56 var str4 = string.Intern(str1 + "1234567");
57
58 System.Diagnostics.Debug.Assert(string.ReferenceEquals(str3, str4));
59 }
60
61 /// <summary>
62 /// 5. 驻留的字符串不能被GC回收
63 /// </summary>
64 public static void BuildString5()
65 {
66 BuildString();
67
68 GC.Collect();
69 GC.Collect();
70 }
71
72 /// <summary>
73 /// 6. 驻留的字符串是基于整个进程范围的,而不是基于当前AppDomain。
74 /// </summary>
75 public static void BuildString6()
76 {
77 AppDomainContext.CreateDomainContext("Domain A").Invoke<Foo>(foo => foo.BuildString());
78 AppDomainContext.CreateDomainContext("Domain B").Invoke<Foo>(foo => foo.BuildString());
79 AppDomainContext.CreateDomainContext("Domain C").Invoke<Foo>(foo => foo.BuildString());
80 }
81
82 public class AppDomainContext
83 {
84 public AppDomain AppDomain { get; private set; }
85 private AppDomainContext(string friendlyName)
86 {
87 this.AppDomain = AppDomain.CreateDomain(friendlyName);
88 }
89
90 public static AppDomainContext CreateDomainContext(string friendlyName)
91 {
92 return new AppDomainContext(friendlyName);
93 }
94
95 public void Invoke<T>(Action<T> action)
96 {
97 T instance = (T)this.AppDomain.CreateInstanceAndUnwrap(typeof(T).Assembly.FullName, typeof(T).FullName);
98 action(instance);
99 }
100 }
101
102 public class Foo : MarshalByRefObject
103 {
104 public void BuildString()
105 {
106 var str1 = "ABCDEFG";
107 var str2 = "ABCDEFG";
108 var str3 = "1234567";
109 var str4 = "1234567";
110 }
111 }
112 }
2 {
3 /// <summary>
4 /// 1. 具有相同字符序列的String对象不会重复创建
5 /// </summary>
6 public static void BuildString()
7 {
8 var str1 = "ABCDEFG";
9 var str2 = "ABCDEFG";
10 var str3 = "1234567";
11 var str4 = "1234567";
12
13 GC.Collect();
14 System.Diagnostics.Debug.Assert(string.ReferenceEquals(str1, str2));
15 }
16
17 /// <summary>
18 /// 2. 字符串驻留机制同样于string literal + string literal的运算
19 /// </summary>
20 public static void BuildString2()
21 {
22 var str1 = "ABCDEFG";
23 var str2 = "ABCD" + "EFG";
24 var str3 = "1234567";
25 var str4 = "1234" + "567";
26
27 System.Diagnostics.Debug.Assert(string.ReferenceEquals(str1, str2));
28 }
29
30 /// <summary>
31 /// 3. 字符串驻留机智不适合Variable + string literal形式
32 /// </summary>
33 public static void BuildString3()
34 {
35 ///无论是变量和一个字符串常量相加,还是两个字符串常量相加,运算的结果“ABCDEFG1234678”
36 ///并没有被驻留下来(实际上此时它已经是一个垃圾对象,GC可以对其进行回收)。
37 var str1 = "ABCDEFG";
38 var str2 = "1234567";
39 var str3 = "ABCDEFG" + str2;
40 var str4 = str1 + "1234567";
41 var str5 = str1 + str2;
42
43 System.Diagnostics.Debug.Assert(string.ReferenceEquals(str3, str4));
44 }
45
46 /// <summary>
47 /// 4. 调用string.Intern可以对运算结果进行强制驻留
48 /// </summary>
49 public static void BuildString4()
50 {
51 ///虽然涉及到变量的字符串连接运算结果不会被驻留,但是我们可以通过调用string.Intern方法对其进行强制驻留,
52 ///该方法会迫使传入传入参数表示的字符串被保存到驻留池中
53 var str1 = "ABCDEFG";
54 var str2 = "1234567";
55 var str3 = string.Intern("ABCDEFG" + str2);
56 var str4 = string.Intern(str1 + "1234567");
57
58 System.Diagnostics.Debug.Assert(string.ReferenceEquals(str3, str4));
59 }
60
61 /// <summary>
62 /// 5. 驻留的字符串不能被GC回收
63 /// </summary>
64 public static void BuildString5()
65 {
66 BuildString();
67
68 GC.Collect();
69 GC.Collect();
70 }
71
72 /// <summary>
73 /// 6. 驻留的字符串是基于整个进程范围的,而不是基于当前AppDomain。
74 /// </summary>
75 public static void BuildString6()
76 {
77 AppDomainContext.CreateDomainContext("Domain A").Invoke<Foo>(foo => foo.BuildString());
78 AppDomainContext.CreateDomainContext("Domain B").Invoke<Foo>(foo => foo.BuildString());
79 AppDomainContext.CreateDomainContext("Domain C").Invoke<Foo>(foo => foo.BuildString());
80 }
81
82 public class AppDomainContext
83 {
84 public AppDomain AppDomain { get; private set; }
85 private AppDomainContext(string friendlyName)
86 {
87 this.AppDomain = AppDomain.CreateDomain(friendlyName);
88 }
89
90 public static AppDomainContext CreateDomainContext(string friendlyName)
91 {
92 return new AppDomainContext(friendlyName);
93 }
94
95 public void Invoke<T>(Action<T> action)
96 {
97 T instance = (T)this.AppDomain.CreateInstanceAndUnwrap(typeof(T).Assembly.FullName, typeof(T).FullName);
98 action(instance);
99 }
100 }
101
102 public class Foo : MarshalByRefObject
103 {
104 public void BuildString()
105 {
106 var str1 = "ABCDEFG";
107 var str2 = "ABCDEFG";
108 var str3 = "1234567";
109 var str4 = "1234567";
110 }
111 }
112 }