字符串缓存。 内存优化和重用

我目前正在开发一个非常大的遗留应用程序,它处理从各种来源收集的大量字符串数据(IE,名称,标识符,与业务相关的公共代码等)。 仅此数据在应用程序中可能需要多达200兆的内存。

我的一位同事提到了一种减少内存占用的可能策略(因为许多单个字符串在数据集中是重复的),将在字典中“缓存”重复的字符串并在需要时重新使用它们。 所以例如……

public class StringCacher() { public readonly Dictionary _stringCache; public StringCacher() { _stringCache = new Dictionary(); } public string AddOrReuse(string stringToCache) { if (_stringCache.ContainsKey(stringToCache) _stringCache[stringToCache] = stringToCache; return _stringCache[stringToCache]; } } 

然后使用这个缓存……

 public IEnumerable IncomingData() { var stringCache = new StringCacher(); var dataList = new List(); // Add the data, a fair amount of the strings will be the same. dataList.Add(stringCache.AddOrReuse("AAAA")); dataList.Add(stringCache.AddOrReuse("BBBB")); dataList.Add(stringCache.AddOrReuse("AAAA")); dataList.Add(stringCache.AddOrReuse("CCCC")); dataList.Add(stringCache.AddOrReuse("AAAA")); return dataList; } 

由于字符串是不可变的,并且框架完成了许多内部工作以使它们以类似于值类型的方式工作,我一半认为这只会将每个字符串的副本创建到字典中并且只需将其加倍使用的内存而不是仅仅传递对字典中存储的字符串的引用(这是我的同事所假设的)。

因此,考虑到这将在大量字符串数据上运行…

  • 这是否会节省任何内存,假设30%的字符串值将被使用两次或更多次?

  • 假设这甚至可以正常工作吗?

这基本上就是字符串实习,除了你不必担心它是如何工作的。 在您的示例中,您仍然在创建一个字符串,然后比较它,然后将副本丢弃。 .NET将在运行时为您执行此操作。

另请参见String.Intern和优化C#字符串性能(C Calvert)

如果使用如第(18行和第19行)所示的代码( String goober1 = "foo"; String goober2 = "foo"; )创建新字符串,则会检查实习生表。 如果你的字符串已经存在,那么两个变量都将指向由intern表维护的同一块内存。

所以,你不必自己动手 – 它不会真正提供任何优势。 编辑除外:你的字符串通常不会存在,只要你的AppDomain – 实体字符串在AppDomain的生命周期中存在,这对于GC来说不一定很好。 如果你想要短暂的字符串,那么你想要一个游泳池。 来自String.Intern

如果您尝试减少应用程序分配的内存总量,请记住,实习字符串有两个不需要的副作用。 首先,在公共语言运行时(CLR)终止之前,不太可能释放为被占用的String对象分配内存 。 原因是CLR对interned String对象的引用可以在应用程序甚至应用程序域终止后持久存在。 …

编辑2另见Jon Skeets SO回答这里

这已经是内置的.NET,它叫做String.Intern ,不需要重新发明。

您可以使用内置的.Netfunction实现此目的。

初始化字符串时,使用字符串调用string.Intern()。

例如:

 dataList.Add(string.Intern("AAAA")); 

具有相同字符串的每个后续调用将在内存中使用相同的引用。 因此,如果您有1000个AAAA,则只有1个AAAA副本存储在内存中。

请阅读有关String Interning并使用.Net已有的此function:有如下许多文章可以理解如下:

字符串实习从维基百科

理解C#:String.Intern使字符串变得有趣

SO QA

Eric Lippert的弦乐实习