13

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

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

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% 的字符串值将被使用两次或更多,这是否会节省内存?

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

4

3 回答 3

12

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

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

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

所以,你不必自己动手——它不会真正提供任何优势。编辑,除非:您的字符串通常不会像您的 AppDomain 那样存在 - 实习字符串在 AppDomain 的生命周期内存在,这对于 GC 不一定很好。如果你想要短命的字符串,那么你想要一个池。来自String.Intern

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

编辑 2另见Jon Skeets SO answer here

于 2013-05-19T15:43:01.890 回答
3

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

于 2013-05-19T15:39:24.700 回答
3

您可以使用内置的 .Net 功能来实现这一点。

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

例如:

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

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

于 2013-05-19T15:46:22.127 回答