C# string 防止GC及内存分配

目录

前言:

字符串的内存分配

如何理解C#字符串常量池

如何防止string产生GC

C# 使用注意情况 


前言:

在 C# 中,字符串类型是一个引用类型,它的内存分配方式与其他引用类型类似,都是在堆上分配内存。

字符串的内存分配

字符串类型的内存分配方式有以下几种:

  1. 字符串常量池:字符串常量池是一块特殊的内存区域,用于存储字符串常量。当我们定义一个字符串常量时,它会被分配到字符串常量池中。如果我们定义多个相同的字符串常量,它们会共享同一个内存地址,从而节省内存空间。

  2. 字符串字面量:字符串字面量是一种特殊的字符串常量,它们是在编译时就确定的字符串常量。当我们使用字符串字面量时,它们会被分配到字符串常量池中。

    扫描二维码关注公众号,回复: 15329575 查看本文章
  3. 字符串对象:字符串对象是通过 new 关键字创建的字符串实例。当我们创建一个字符串对象时,它会被分配到堆上的一个新的内存地址。

需要注意的是,由于字符串是不可变类型,所以在对字符串进行修改时,会创建一个新的字符串对象,从而增加内存分配的开销。为了避免这种情况,可以使用 StringBuilder 类来优化字符串操作,它可以在内部维护一个可变的字符串缓冲区,从而避免创建新的字符串对象。

下面是一个示例代码,演示了字符串的内存分配方式:

string str1 = "hello"; // 字符串常量池
string str2 = "hello"; // 字符串常量池,与 str1 共享同一个内存地址
string str3 = new string('qqqq', 100); // 堆上分配内存
string str4 = "hello" + "world"; // 字符串常量池,编译时就确定了
string str5 = str1 + str2; // 堆上分配内存
StringBuilder sb = new StringBuilder(); // 堆上分配内存
sb.Append("hello");
sb.Append("world");
string str6 = sb.ToString(); // 堆上分配内存,但是避免了创建多个字符串对象

 在上面的代码中,我们定义了多个字符串变量,演示了字符串的内存分配方式。需要注意的是,字符串常量池和堆上分配内存的方式都会受到垃圾回收器的影响,如果字符串对象没有被引用,它们会被垃圾回收器回收。

如何理解C#字符串常量池

C# 字符串常量池是一种特殊的内存区域,用于存储字符串常量。在 .NET Framework 中,字符串常量池是由运行时环境维护的,它的作用是尽可能地重用字符串常量,从而节省内存空间。

当我们定义一个字符串常量时,它会被分配到字符串常量池中。如果我们定义多个相同的字符串常量,它们会共享同一个内存地址,从而节省内存空间。例如:

string str1 = "hello";
string str2 = "hello";

在上面的代码中,str1 和 str2 都是字符串常量,它们的值相同,因此它们会共享同一个内存地址。这意味着,str1 和 str2 本质上是同一个字符串对象,它们的引用指向同一个内存地址。

需要注意的是,字符串常量池只适用于字符串常量,它不适用于字符串对象。例如:

string str3 = new string('a', 10);
string str4 = new string('a', 10);

在上面的代码中,str3 和 str4 都是字符串对象,它们的值相同,但是它们并不共享同一个内存地址,因为它们是通过 new 关键字创建的。这意味着,str3 和 str4 本质上是不同的字符串对象,它们的引用指向不同的内存地址。

需要注意的是,字符串常量池是有大小限制的,如果字符串常量池中已经存在了大量的字符串常量,新的字符串常量可能会被分配到堆上。此外,字符串常量池的大小也会受到垃圾回收器的影响,如果字符串常量没有被引用,它们会被垃圾回收器回收。

如何防止string产生GC

在 C# 中,字符串类型是一个引用类型,它的操作比较常见,因此在编写高性能的 C# 代码时,需要对字符串类型进行优化。下面是一些优化字符串类型的建议:

  1. 避免使用字符串拼接操作,因为字符串拼接操作会创建新的字符串对象,从而增加 GC 的负担。可以使用 StringBuilder 类来优化字符串拼接操作。

  2. 避免使用字符串的 + 运算符,因为它会创建新的字符串对象,从而增加 GC 的负担。可以使用 string.Concat 方法或 string.Join 方法来优化字符串拼接操作。

  3. 避免使用字符串的 Substring 方法,因为它会创建新的字符串对象,从而增加 GC 的负担。可以使用 string.AsSpan 方法来避免创建新的字符串对象。

  4. 避免使用字符串的 ToLower 或 ToUpper 方法,因为它们会创建新的字符串对象,从而增加 GC 的负担。可以使用 string.Create 方法和 string.Span 来避免创建新的字符串对象。

  5. 使用字符串常量或字符串字面量来代替字符串变量,因为字符串常量和字符串字面量会被分配到字符串常量池中,从而可以重用已有的字符串对象,减少内存分配的开销。

  6. 将字符串的长度缓存到一个变量中,避免在循环中多次计算字符串的长度,从而减少计算时间和 CPU 负担。

  7. 避免使用 string.Format() 方法,因为它会创建新的字符串对象,从而增加 GC 的负担。可以使用插值字符串或 StringBuilder 类来代替 string.Format() 方法。

  8. 避免在字符串操作中使用大量的临时字符串,因为它会增加 GC 的负担。可以使用 StringBuilder 类来优化字符串拼接操作。

下面是一个示例代码,演示了如何优化字符串类型:

string str1 = "hello";
string str2 = "world";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.Append(str1);
    sb.Append(str2);
}
string str3 = sb.ToString();
int length = str3.Length;
int index = str3.IndexOf(str1, StringComparison.OrdinalIgnoreCase);

在上面的代码中,我们使用了 StringBuilder 类来优化字符串拼接操作,使用了字符串的 Length 属性来代替 Count() 方法,使用了字符串的 IndexOf() 方法来代替 Contains() 方法。我们还避免使用了字符串的 ToUpper() 和 ToLower() 方法,使用了 StringComparison.OrdinalIgnoreCase 来代替大小写转换操作。 


C# string使用注意情况 

  1. 避免使用字符串的 GetHashCode() 方法,因为字符串的 GetHashCode() 方法计算哈希值时会考虑字符串的长度和内容,从而增加计算时间和 CPU 负担。可以使用 FNV-1a 算法或 MurmurHash 算法来代替字符串的 GetHashCode() 方法。

  2. 使用字符串的 IndexOf() 方法来代替 Contains() 方法,因为 IndexOf() 方法可以指定搜索的起始位置,从而减少搜索时间。

  3. 使用字符串的 Length 属性来代替 Count() 方法,因为 Length 属性是一个字段,而 Count() 方法是一个方法,它需要遍历整个字符串来计算长度。

  4. 尽量使用静态字符串方法,如 string.IsNullOrEmptystring.IsNullOrWhiteSpacestring.Compare 等,它们具有更好的性能和更低的内存分配开销。

猜你喜欢

转载自blog.csdn.net/lejian/article/details/131244936