我目前正在开发一个非常大的遗留应用程序,它处理从各种来源收集的大量字符串数据(IE,名称,标识符,与业务相关的公共代码等)。在申请过程中,仅此数据就可能需要200兆的内存。
我的一位同事提到了一种减少内存占用的可能策略(因为很多单个字符串在数据集中都是重复的),就是将字典中的重复字符串“缓存”并在字典中重复使用它们。需要。例如......
public class StringCacher()
{
public readonly Dictionary<string, string> _stringCache;
public StringCacher()
{
_stringCache = new Dictionary<string, string>();
}
public string AddOrReuse(string stringToCache)
{
if (_stringCache.ContainsKey(stringToCache)
_stringCache[stringToCache] = stringToCache;
return _stringCache[stringToCache];
}
}
然后使用此缓存...
public IEnumerable<string> IncomingData()
{
var stringCache = new StringCacher();
var dataList = new List<string>();
// 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%的字符串值将被使用两次或更多?
假设这甚至可以正常工作吗?
答案 0 :(得分:9)
这基本上就是字符串实习,除了你不必担心它是如何工作的。在您的示例中,您仍然在创建一个字符串,然后比较它,然后将副本丢弃。 .NET将在运行时为您完成此任务。
另请参阅String.Intern
和Optimizing C# String Performance (C Calvert)
如果使用第18行和第19行中显示的代码(
String goober1 = "foo"; String goober2 = "foo";
)创建新字符串,则会检查实习生表。如果你的字符串已经在那里,那么两个变量都将指向由实习生表维护的同一块内存。
所以,你不必自己动手 - 它不会真正提供任何优势。 编辑除非:您的字符串通常不会存在,只要您的AppDomain - 实体字符串在AppDomain的生命周期内存在,这对于GC来说不一定很好。如果你想要短暂的字符串,那么你想要一个游泳池。来自String.Intern
:
如果您尝试减少应用程序分配的内存总量,请记住,实习字符串有两个不需要的副作用。首先,在公共语言运行库(CLR)终止之前,不太可能释放为已实例化的String对象分配的内存。原因是CLR对interned String对象的引用可以在应用程序甚至应用程序域终止后继续存在。 ...
答案 1 :(得分:3)
这已经是内置的.NET,它被称为String.Intern
,无需重新发明。
答案 2 :(得分:2)
您可以使用内置的.Net功能来实现此目的。
初始化字符串时,请使用字符串调用string.Intern()。
例如:
dataList.Add(string.Intern("AAAA"));
每次使用相同字符串的后续调用都将在内存中使用相同的引用。因此,如果您有1000个AAAA,则只有1个AAAA副本存储在内存中。
答案 3 :(得分:0)
请阅读有关String Interning
的信息,并使用.Net
中已有的此功能:有如下文章可供理解如下:
String interning From Wikipedia