【问题标题】:String caching. Memory optimization and re-use字符串缓存。内存优化和重用
【发布时间】:2013-05-14 05:37:39
【问题描述】:

我目前正在开发一个非常大的遗留应用程序,它处理从各种来源(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% 的字符串值将被使用两次或更多,这是否会节省内存?

  • 这样的假设是否正确?

【问题讨论】:

  • 这是一个错误,30% 不足以证明让你的程序慢一百倍。 RAM 便宜又丰富,8 GB 需要 67 美元。 1.64 美元你写不出一行代码
  • +1 @HansPassant 计算时间与 RAM 投资回报率。
  • @HansPassant 感谢您指出这一点。我会确保在实施时进行性能测试。我同意,这些天你的普通 PC 中的内存非常便宜,但不幸的是,当谈到大型金融机构的生产工作站时,所有内存(和任何其他部件)都必须通过特定的供应商购买和安装,推动每个工作站 8 gig 到 500 多美元的实际成本。将其乘以 1000 多个用户,您就会明白为什么机器升级不是一个真正的选择。

标签: c# .net


【解决方案1】:

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

另请参阅String.InternOptimizing C# String Performance (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

【讨论】:

  • 一组好的数据可能不会在应用程序的生命周期内出现,所以在这种情况下,将它们存储在字典中会更有效,当不再需要数据集。
  • 听起来很明智。字符串实习非常适合在代码中定义的文字和常量,对于占用大量空间并可以从“重复数据删除”中受益的本地化字符串,对于将字符串保存在内存中的CMS 风格的应用程序。但是,例如,如果您要从 Web 服务器中提取 html,处理其中的部分,然后将它们全部丢弃,那么使用重复数据删除池可能会更好。
  • @Moog,另请注意:_stringCache[stringToCache] = stringToCache; 正如您所写的那样,可能会重复该字符串(一次用于键,一次用于值),我不确定,因为我快用完了门 - 但值得检查。
  • @Moog。不,刚刚检查了 BCL 代码 - 据我所知你没问题。 Dictionary&lt;string, string&gt; 除了计算和存储字符串哈希码并使用它来加速比较(因此对于冗长的字符串,这实际上可能是一个好处)之外,并没有什么时髦的东西。
  • ".NET 将在运行时为您执行此操作" -- 不,它不会。您引用的示例涉及在编译时处理的字符串 literals。除非用户代码通过调用string.Intern() 方法显式完成,否则不会在运行时完成字符串的驻留。
【解决方案2】:

这已经是.NET内置了,叫String.Intern,不用重新发明。

【讨论】:

  • 好的,太好了,我不知道!那么使用这种方法缓存字符串会对内存占用有显着影响吗?如果在数十万个字符串上调用 String.Intern 会不会对性能有很大影响?
  • 它应该减少内存消耗并提高性能。您需要对其进行测试才能看到对您的应用程序的影响。
  • @Moog。不过要小心 - 实习字符串在 AppDomain 的整个生命周期内都存在,因此它们不会被 GC 处理。如果您想要短命的字符串,您的池子想法可能会更好(请参阅我的回答中的评论)
【解决方案3】:

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

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

例如:

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

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

【讨论】:

    猜你喜欢
    • 2016-09-01
    • 2015-01-19
    • 2019-11-12
    • 1970-01-01
    • 2010-10-18
    • 1970-01-01
    • 2022-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多