昨天调试一段程序发现内存始终释放不掉最后终于发现是对String 的错误使用造成这促使我今天又仔细研究了一下String类型不研究不知道一研究发现我过去对String 的很多认识都是错误的感觉这种错误认识还比较有典型性于是写下此文和大家一起探讨 String 类型变量追加或修改后的新String对象是驻留(Interned)的 如下面代码 string s = abcd;string s = s + e; 我过去想当然的认为s 是驻留的但实际上并非如此用 stringIsInterned 方法检测s是非驻留的后来研究发现只有常量字符串才会默认驻留其他的字符串变量哪怕是采用 new string 构造出来的默认都非驻留除非用stringIntern 强行驻留后面我将提到驻留对内存的影响微软之所以不让所有的字符串都驻留我认为还是处于内存方面的考虑 String 变量不再引用后CLR会通过GC自动释放其内存 string s = abcd;s = null; 上面代码我想当然的认为s = null 后已经不再对 abcd 这个字符串引用如果没有其他引用指向这个字符串GC会释放abcd这块内存实际结果却是否定的因为s 被赋予了一个常量导致 abcd这个字符串是驻留的驻留的字符串在进程结束之前无法被自动释放更糟糕的是我昨天调试的那段程序里面大量的字符串变量被采用 stringIntern 强制驻留这导致我把所有的托管对象都释放了依然无法释放那部分大概多M的内存 Performance Considerations If you are trying to reduce the total amount of memory your application allocates keep in mind that interning a string has two unwanted side effects First the memory allocated for interned String objects is not likely be released until the common language runtime (CLR) terminates The reason is that the CLRs reference to the interned String object can persist after your application or even your application domain terminates Second to intern a string you must first create the string The memory used by the String object must still be allocated even though the memory will eventually be garbage collected The NET Framework version introduces the CompilationRelaxations::NoStringInterning enumeration member The NoStringInterning member marks an assembly as not requiring stringliteral interning You can apply NoStringInterning to an assembly using the CompilationRelaxationsAttribute attribute Also when you use the Native Image Generator (Ngenexe) to compile an assembly in advance of run time strings are not interned across modules 看了英文的帮助就知道Intern 后的字符串是无法释放的了 两个String如果引用不同只能用Equal 比较 我一直想当然的认为 两个String 类型如果用 == 操作符比较将比较其引用所以如果两个String引用不同则只能使用Equal 来比较它们是否相等 比如下面语句 string s = new StringBuilder()Append(My)Append(Test)ToString();string s = new StringBuilder()Append(My)Append(Test)ToString(); 如下方法比较其引用 ConsoleWriteLine((object)s == (object)s); 得到结果为 false即s s指向不同引用 那么我想当然的认为 ConsoleWriteLine(s == s); 的结果也是false因为string 是引用类型用==操作符比较引用类型变量如果两个变量的引用不同即便值相同也会返回false 然而运行的结果让我大跌眼镜返回的值是true 于是在网上狂搜最后终于找到了原因 String 的等号操作符的处理是特殊的其源码如下 === Equality operator on string type (C#) === // The == operator overload MSIL: method public hidebysig specialname static bool op_Equality(string a string b) cil managed { maxstack L_: ldarg L_: ldarg L_: call bool SystemString::Equals( string string) L_: ret } [] [] |