Short Question
我有一个运行180,000次的循环 . 在每次迭代结束时,它应该将结果附加到TextBox,TextBox会实时更新 .
使用 MyTextBox.Text += someValue
会导致应用程序占用大量内存,并且在几千条记录之后它耗尽了可用内存 .
有没有更有效的方法将文本附加到180,000次_166349?
Edit 我真的不关心这个特定情况的结果,但是我想知道为什么这似乎是一个内存耗尽,并且如果有一种更有效的方法将文本附加到TextBox .
Long (Original) Question
我有一个小应用程序,它读取CSV文件中的ID号列表,并为每个应用程序生成PDF报告 . 生成每个pdf文件后, ResultsTextBox.Text
会附加已处理的报告的ID号,并且已成功处理该报告 . The process runs on a background thread, so the ResultsTextBox gets updated real-time as items get processed
我目前正在针对180,000个ID号运行该应用程序,但是应用程序占用的内存随着时间的推移呈指数级增长 . 它从大约90K开始,但是大约3000条记录占用大约250MB,而4000条记录占用大约500 MB的内存 .
如果我注释掉结果文本框的更新,内存保持相对静止大约90K,所以我可以假设写 ResultsText.Text += someValue
是导致它吃内存的原因 .
我的问题是,这是为什么?将数据附加到不占用内存的TextBox.Text的更好方法是什么?
我的代码如下所示:
try
{
report.SetParameterValue("Id", id);
report.ExportToDisk(ExportFormatType.PortableDocFormat,
string.Format(@"{0}\{1}.pdf", new object[] { outputLocation, id}));
// ResultsText.Text += string.Format("Exported {0}\r\n", id);
}
catch (Exception ex)
{
ErrorsText.Text += string.Format("Failed to export {0}: {1}\r\n",
new object[] { id, ex.Message });
}
还应该值得一提的是,该应用程序是一次性的事情并不重要,它需要花费几个小时(或几天:)来生成所有报告 . 我主要担心的是,如果它达到系统内存限制,它将停止运行 .
我没关系更新结果TextBox注释掉来运行这个东西,但我想知道是否有一种更有效的内存方式将数据附加到 TextBox.Text
以用于未来的项目 .
12 回答
我怀疑内存使用量如此之大的原因是因为文本框保持堆栈,以便用户可以撤消/重做文本 . 在您的情况下似乎不需要该功能,因此请尝试将IsUndoEnabled设置为false .
使用
TextBox.AppendText(someValue)
而不是TextBox.Text += someValue
. 它's easy to miss since it'在TextBox上,而不是TextBox.Text . 与StringBuilder一样,这将避免在每次添加内容时创建整个文本的副本 .看看这与keyboardP的答案中的
IsUndoEnabled
标志相比如何,这将会很有趣 .不要直接附加到text属性 . 使用StringBuilder进行追加,然后在完成后,将.text设置为stringbuilder中的完成字符串
我没有使用文本框,而是执行以下操作:
打开文本文件并将错误流式传输到日志文件以防万一 .
使用列表框控件来表示错误,以避免复制潜在的大量字符串 .
就个人而言,我总是使用string.Concat * . 我记得在几年前的Stack Overflow上读过一个问题,它有比较常用方法的分析统计数据,并且(似乎)回想起
string.Concat
赢了 .尽管如此,我能找到的最好的是this reference question和这个特定的String.Format vs. StringBuilder问题,其中提到
String.Format
在内部使用StringBuilder
. 这让我想知道你的记忆力是否在其他地方 .**基于James的评论,我应该提到我从不做重字符串格式化,因为我专注于基于Web的开发 . *
也许重新考虑TextBox?包含字符串Items的ListBox可能会表现得更好 .
但主要问题似乎是要求,显示180,000项不能针对(人)用户,也不是在“实时”中改变它 .
最好的方法是显示数据样本或进度指示器 .
当您想要将其转储给糟糕的用户时,批量字符串更新 . 没有用户可以每秒识别超过2或3个更改 . 因此,如果你产生100 /秒,那么组成50 .
一些回应已经提到过,但没有人直截了当地说出这是令人惊讶的 . 字符串是不可变的,这意味着String在创建后无法修改 . 因此,每次连接到现有String时,都需要创建一个新的String对象 . 显然需要创建与该String对象关联的内存,随着您的Strings变得越来越大,这可能会变得昂贵 . 在大学里,我曾经做过业余错误,在一个Java程序中连接Strings,这个程序做了Huffman编码压缩 . 当你连接非常大量的文本时,当你可以简单地使用StringBuilder时,字符串连接真的会伤害你,正如这里提到的那样 .
按照建议使用StringBuilder . 尝试估计最终的字符串大小,然后在实例化StringBuilder时使用该数字 . StringBuilder sb = new的StringBuilder(estSize);
更新TextBox时,只需使用赋值,例如:textbox.text = sb.ToString();
注意上面的跨线程操作 . 但是请使用BeginInvoke . UI更新时无需阻止后台线程 .
A)简介:已经提到过,使用
StringBuilder
B)要点:不要太频繁更新,即
C)如果是一次性工作,请使用x64架构以保持2Gb限制 .
ViewModel
中的StringBuilder将避免字符串重新绑定混乱并将其绑定到MyTextBox.Text
. 此方案将多次提高性能并减少内存使用量 .没有提到的是,即使你在后台线程中执行操作,UI元素本身的更新也要发生在主线程本身上(无论如何都在WinForms中) .
更新文本框时,您是否有任何类似的代码
如果是这样,那么你的后台操作肯定会受到UI更新的瓶颈 .
我建议您的后台操作如上所述使用StringBuilder,但不是每个周期更新文本框,请尝试定期更新它以查看它是否为您提高性能 .
编辑注意:没有使用WPF .
你说记忆成倍增长 . 不,它是quadratic growth,即多项式增长,它不像指数增长那样引人注目 .
您正在创建包含以下项目数的字符串:
使用
n = 180,000
,您可以获得16,200,090,000 items
的总内存分配,即16.2 billion items
!这个内存不会立刻分配,但GC(垃圾收集器)的清理工作很多!另外,请记住,前一个字符串(正在增长)必须复制到新字符串179,999次 . 复制的字节总数也与
n^2
一致!正如其他人所建议的那样,请使用ListBox . 在这里,您可以附加新字符串而无需创建大字符串 .
StringBuild
没有帮助,因为您也想显示中间结果 .