在之前的文章中,我们介绍了dotnet在字符串拼接时可以使用的一些性能优化技巧 。比如:
- 为
StringBuilder
设置Buffer初始大小 - 使用
ValueStringBuilder
等等不过这些都多多少少有一些局限性 , 比如StringBuilder
还是会存在new StringBuilder()
这样的对象分配(包括内部的Buffer) 。ValueStringBuilder
无法用于async/await
的上下文等等 。都不够的灵活 。
StringBuilder
那样用于async/await
的上下文中,又能减少内存分配呢?其实这可以用到存在很久的一个Tips , 那就是想办法复用
StringBuilder
。目前来说复用StringBuilder
推荐两种方式:- 使用ObjectPool来创建
StringBuilder
的对象池 - 如果不想单独创建一个对象池 , 那么可以使用
StringBuilderCache
ObjectPool
,因为它是一个泛型类,可以对任何类型进行池化 。使用方式也非常的简单 , 只需要在引入如下nuget包:dotnet add package Microsoft.Extensions.ObjectPool
Nuget包中提供了默认的StringBuilder
池化策略StringBuilderPooledObjectPolicy
和CreateStringBuilderPool()
方法,我们可以直接使用它来创建一个ObjectPool:var provider = new DefaultObjectPoolProvider();// 配置池中StringBuilder初始容量为256// 最大容量为8192,如果超过8192则不返回池中,让GC回收var pool = provider.CreateStringBuilderPool(256, 8192);var builder = pool.Get();try{ for (int i = 0; i < 100; i++) {builder.Append(i); } builder.ToString().Dump();}finally{ // 将builder归还到池中 pool.Return(builder);}
运行结果如下图所示:文章插图
当然,我们在ASP.NET Core等环境中可以结合微软的依赖注入框架使用它,为你的项目添加如下NuGet包:
dotnet add package Microsoft.Extensions.DependencyInjection
然后就可以写下面这样的代码,从容器中获取ObjectPoolProvider
达到同样的效果:var objectPool = new ServiceCollection() .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>() .BuildServiceProvider() .GetRequiredService<ObjectPoolProvider>() .CreateStringBuilderPool(256, 8192);var builder = objectPool.Get();try{ for (int i = 0; i < 100; i++) {builder.Append(i); } builder.ToString().Dump();}finally{ objectPool.Return(builder);}
更加详细的内容可以阅读蒋老师关于ObjectPool
的系列文章 。使用StringBuilderCache另外一个方案就是在.NET中存在很久的类,如果大家翻阅过.NET的一些代码,在有字符串拼接的场景可以经常见到它的身影 。但是它和
ValueStringBuilder
一样不是公开可用的,这个类叫StringBuilderCache
。文章插图
下方所示就是它的源码,源码链接点击这里:
namespace System.Text{/// <summary>为每个线程提供一个缓存的可复用的StringBuilder的实例</summary>internal static class StringBuilderCache{// 这个值360是在与性能专家的讨论中选择的,是在每个线程使用尽可能少的内存和仍然覆盖VS设计者启动路径上的大部分短暂的StringBuilder创建之间的折衷 。internal const int MaxBuilderSize = 360;private const int DefaultCapacity = 16; // == StringBuilder.DefaultCapacity[ThreadStatic]private static StringBuilder? t_cachedInstance;// <summary>获得一个指定容量的StringBuilder.</summary> 。// <remarks>如果一个适当大小的StringBuilder被缓存了,它将被返回并清空缓存 。public static StringBuilder Acquire(int capacity = DefaultCapacity){if (capacity <= MaxBuilderSize){StringBuilder? sb = t_cachedInstance;if (sb != null){// 当请求的大小大于当前容量时,// 通过获取一个新的StringBuilder来避免Stringbuilder块的碎片化if (capacity <= sb.Capacity){t_cachedInstance = null;sb.Clear();return sb;}}}return new StringBuilder(capacity);}/// <summary>如果指定的StringBuilder不是太大,就把它放在缓存中</summary>public static void Release(StringBuilder sb){if (sb.Capacity <= MaxBuilderSize){t_cachedInstance = sb;}}/// <summary>ToString()的字符串生成器,将其释放到缓存中 , 并返回生成的字符串 。</summary>public static string GetStringAndRelease(StringBuilder sb){string result = sb.ToString();Release(sb);return result;}}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- .net 温故知新:【8】.NET 中的配置从xml转向json
- UE优化性能
- 来啦来啦|开源 * 安全 * 赋能 - .NET Conf China
- 三星s21ultra和苹果12promax性能对比_三星s21ultra和苹果12promax谁的性能好
- 如何在.NET程序崩溃时自动创建Dump?
- .NET Conf 2022 &ndash; 11 月 8 日至 10 日
- .NET 零开销抽象指南
- 二、.Net Core搭建Ocelot
- 创建.NET程序Dump的几种姿势
- m1x芯片和m1区别_哪个性能更强