【问题标题】:How to free, or recycle, a string in C#?如何在 C# 中释放或回收字符串?
【发布时间】:2011-11-11 14:24:50
【问题描述】:

我有一个大字符串(例如 20MB)。

我现在正在解析这个字符串。问题是 C# 中的字符串是不可变的。这意味着一旦我创建了一个子字符串并查看它,内存就被浪费了。

由于所有的处理,内存被我不再使用、需要或引用的 String 对象堵塞;但是垃圾收集器需要很长时间才能释放它们。

所以应用程序内存不足。

我可以使用性能不佳的 club 方法,然后调用几千次:

GC.Collect();

无处不在,但这并不能真正解决问题。

我知道StringBuilder创建 一个大字符串时存在。

我知道TextReader 的存在是为了将String 读入char 数组。

我需要以某种方式“重用”一个字符串,使其不再是不可变的,这样我就不会在 1k 的情况下不必要地分配千兆字节的内存。

【问题讨论】:

  • 你不能,除非你把它钉住并且变得不安全(你可以直接用不安全的代码修改缓冲区)。我认为您可能需要使用流并且一次只读取一点点
  • 根据您对数据的处理方式,实现您自己的“字符串”类可能是有意义的,其中子字符串实际上是对父字符串的引用(类似于 Java 对 @987654329 所做的@)。这样,只有原始字符串数据存储在内存中。你可能也想看看这篇文章:stackoverflow.com/questions/6742923/…
  • 我猜你还有一些你不知道的参考资料。
  • 如果你正在制作一千万个字符串,那么很有可能你做错了什么。为什么你首先在内存中有这么大的字符串?您是否需要将整个内容保存在内存中才能解析它?解析器通常以仅向前的方式使用字符串,并具有有限的前瞻性;为什么你需要一次在内存中的整个字符串?

标签: c# string garbage-collection immutability


【解决方案1】:

如果您的应用程序正在死机,那可能是因为您仍然有对字符串的引用——而不是因为垃圾收集器没有清理它们。我已经看到它失败了,但这不太可能。您是否使用分析器来检查您是否真的确实在内存中一次有很多字符串?

总而言之,你不能重用一个字符串来存储不同的数据——这是做不到的。如果你愿意,你可以编写自己的等价物——但有效且正确地做到这一点的机会非常渺茫。现在,如果您可以提供有关您正在做什么的更多信息,我们或许可以建议不使用太多内存的替代方法。

【讨论】:

  • Jon 很可能是正确的,并且您持有对字符串的某种引用,因此阻止了清理。但是,如果不是这种情况,并且您必须为字符串重用内存,则可以考虑使用“不安全”代码,但这只是作为最后的手段。你可以在这里找到更多细节msdn.microsoft.com/en-us/library/aa288474%28v=vs.71%29.aspx
  • 我们本身并不持有参考资料;如果我们强制垃圾收集器运行,内存就会被释放。在某种程度上,您可以存在主义地争辩说我们正在持有内存,因为它分配在我的进程空间中。没有什么可以阻止 GC 释放它 - 只是它运行得不够快。
  • @Ian:这至少是不寻常的。子串有多大?原始大字符串的来源是什么,您可以流式传输它吗(例如一次工作一行)?
  • 它们是MHT 文件;单文件编码的网页;客户将它们用作有关人员信息的序列化格式(例如 FBI 最想要的)。它包含 base-64 编码的图像,足够大,适合面部识别。需要时我们处理几十万个。有时mht 文件会在硬盘或CD 上;但他们也可以来自我们的WebRequest。每个都加载到内存中(作为字符串)并进行处理。
  • @Ian:你能否将它们流式传输,而不是一次性将整个页面加载到内存中?
【解决方案2】:

这个问题已经有将近 10 年的历史了。这些天,请看ReadOnlySpan - 使用AsSpan() 方法从字符串中实例化一个。然后,您可以应用索引运算符将切片作为跨度获取,而无需分配任何新字符串。

【讨论】:

    【解决方案3】:

    我建议,考虑到 您不能在 C# 中重用字符串,请使用 Memory-Mapped Files。您只需将字符串保存在磁盘上,并通过像流这样的映射文件以性能/内存消耗良好的关系对其进行处理。在这种情况下,您重用同一个文件、同一个流,并且只对可能的一小部分数据(如字符串)进行操作,您在那个精确时刻需要这些数据,然后立即将其丢弃。

    此解决方案严格取决于您的项目要求,但我认为您可能会认真考虑的解决方案之一,因为尤其是内存消耗会大幅下降,但您会在性能方面“付出”一些代价。

    【讨论】:

    • 即使文件是内存映射的,我们仍然会遇到将部分读入Strings 的问题 - 有时是大部分,有时是小部分,有时是较大部分中的小部分。最终,所有这些未收集的字符串都会阻塞可用内存 - 或导致交换死亡。
    • @Ian 频繁但短期使用相对较小的字符串,因为在这种情况下您不需要将所有数据加载到内存中,对我来说应该会有很大的不同。
    【解决方案4】:

    您是否有一些示例代码来测试可能的解决方案是否能正常工作?

    一般来说,任何大于 85KB 的对象都将被分配到 Large Object Heap,这可能会减少垃圾收集的频率。

    另外,如果你真的很用力地推动 CPU,垃圾收集器可能会不那么频繁地执行它的工作,试图不妨碍你。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-15
      • 1970-01-01
      • 2019-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多