【问题标题】:StringBuilder used with PadLeft/Right OutOfMemoryExceptionStringBuilder 与 PadLeft/Right OutOfMemoryException 一起使用
【发布时间】:2012-11-15 11:31:09
【问题描述】:

所有,我有以下Append,当我为固定文本文件生成一行时,我正在执行它

formattedLine.Append(this.reversePadding ?
                     strData.PadLeft(this.maximumLength) :
                     strData.PadRight(this.maximumLength)); 

此特殊异常发生在 PadLeft() 上,其中 this.maximumLength = 1,073,741,823 [从 SQL Server 收集的 NVARCHAR(MAX) 的字段长度]。 formattedLine = "101102AA-1" 发生异常时,为什么会发生这种情况。我的最大允许长度应该是2,147,483,647?

我想知道https://stackoverflow.com/a/1769472/626442 是否是这里的答案 - 但是,我正在通过适当的Dispose() 调用任何一次性对象和using 块在可能的情况下管理任何内存。 p>

注意。此固定文本导出是在后台线程上完成的。

感谢您的宝贵时间。

【问题讨论】:

  • 你的标题和你的正文不同步 - 你声称它是 Append 正在抛出标题,但在正文中你说它是 PadLeft。我强烈怀疑 Append 在这里无关紧要。
  • 同意。我现在就改一下……
  • 对对象调用Dispose 不会调用垃圾收集器。仅供参考。
  • @recursive 我知道。我理解这一点,我在这里没有建议这样做。不过感谢您的澄清。

标签: c# stringbuilder string-concatenation


【解决方案1】:

这个特殊的异常发生在 PadLeft() 上,其中 this.maximumLength = 1,073,741,823

没错。因此,您正在尝试创建一个包含超过十亿个字符的字符串。

这是行不通的,我非常非常怀疑这是你真正想要做的。

请注意,.NET 中的每个char 都是两个字节,而 .NET 中的 also 字符串以空值结尾...并且在数据之外还有一些其他字段(长度,对于一个)。这意味着您至少需要 2147483652 字节 + 对象开销,这会使您超过每个对象 2GB 的限制。

如果您在 64 位版本的 Windows 上运行,在 .NET 4.5 中,有一个特殊的 app.config 设置 <gcAllowVeryLargeObjects> 允许大于 2GB 的数组。但是,我认为这不会改变您的特定用例:

在您的应用程序配置文件中使用此元素可启用大于 2 GB 的数组,但不会更改对象大小或数组大小的其他限制:

  • 数组中元素的最大数量是 UInt32MaxValue。

  • 对于字节数组和单字节结构的数组,任何一维的最大索引为 2,147,483,591 (0x7FFFFFC7),其他类型的最大索引为 2,146,435,071 (0X7FEFFFFF)。

  • 字符串和其他非数组对象的最大尺寸不变。

无论如何,在创建这样的字符串后,您想对它做什么?

【讨论】:

  • 这应该是评论,而不是答案。此外,他认为他应该有一个 2G 地址空间,因此处理 1G 很可能是他的想法。
  • 关于您的编辑...为什么在 64 位 .NET 实现下需要特殊的 app.config 设置来分配如此大的数组?
  • @EricJ.:说实话,不知道为什么它只是一种选择。
  • 嗨乔恩。你说得对,我并不特别想用那个长度填充固定文本字段,但我目前正在编写一个实用程序来导出任何数据库表。这个字段是 nText 并且它的最大长度是自动收集的。也许我需要考虑限制这个最大长度。感谢您的宝贵时间。
  • @EricJ.:我没有看到任何关于 2G 地址空间的信息 - 我是否在这里错过了评论?无论如何,我希望你现在认为我的回答不仅仅是评论:)
【解决方案2】:

为了为此操作分配内存,操作系统必须找到足够大的连续内存来执行该操作。

内存碎片可能导致这是不可能的,尤其是在使用 32 位 .NET 实现时。

【讨论】:

  • 感谢您的回答。我想过这个原因。但我似乎很好地管理了任何一次性物品。在这里调用 GC 是一个可行的选择,还是这种水?感谢您的宝贵时间...
  • @Killercam:尝试使用 Visual Studio 的内存分析器执行此操作。您将看到内存分配发生了什么。
【解决方案3】:

我认为可能有更好的方法来完成您想要完成的任务。据推测,这个StringBuilder 将被写入一个文件(这就是您的描述听起来的样子),显然,您还可能处理大型(巨大)数据库记录。

您可能会考虑一种流式处理方法,它不需要分配如此巨大的内存块。 为此,您可以调查以下内容:

SqlDataReader 类公开了一个GetChars() 方法,允许您读取单个大记录的一部分。 然后,而不是使用StringBuilder,也许使用StreamWriter(或其他一些TextWriter 派生类)将每个块写入输出。 这只需要一次在您的应用程序的内存空间中拥有一个充满缓冲区的记录。祝你好运!

【讨论】:

  • 这是一个很好的答案,也是我正在做的事情。我正在使用 SqlReader 读取单行(我可以将其缩减为一行中的单个字段)。我发现问题与应用于字段的默认填充有关,例如NTEXT,它可能很大(1,073,741,823)。我想我只需要以更保留的方式处理默认填充...感谢您的时间。
猜你喜欢
  • 2010-12-18
  • 2011-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-09
  • 1970-01-01
相关资源
最近更新 更多