当前位置:  开发笔记 > 编程语言 > 正文

为什么Delphi中字符串的内存过多?

如何解决《为什么Delphi中字符串的内存过多?》经验,为你挑选了3个好方法。

我正在阅读一个大文本文件,其中有140万行,大小为24 MB(平均每行17个字符).

我正在使用Delphi 2009,文件是ANSI但在读取时会转换为Unicode,所以你可以说转换后的文本大小为48 MB.

(编辑:我发现了一个更简单的例子......)

我正在将此文本加载到一个简单的StringList中:

  AllLines := TStringList.Create;
  AllLines.LoadFromFile(Filename);

我发现数据行似乎占用了比48 MB更多的内存.

实际上,他们使用155 MB的内存.

我不介意Delphi使用48 MB甚至多达60 MB允许一些内存管理开销.但155 MB似乎过度.

这不是StringList的错误.我之前尝试将行加载到记录结构中,我得到了相同的结果(160 MB).

我没有看到或理解什么可能导致Delphi或FastMM内存管理器使用3倍的内存量来存储字符串.堆分配不能低效,可以吗?

我调试了这个,并尽可能地研究它.任何关于为什么会发生这种情况的想法,或者可能有助于减少过量使用的想法都会受到高度赞赏.

注意:我使用这个"较小"的文件作为示例.我真的想加载一个320 MB的文件,但是由于这个多余的字符串要求,Delphi要求超过2 GB的RAM和内存不足.

Addenum:Marco Cantu刚刚发布了关于Delphi和Unicode的白皮书.Delphi 2009将每个字符串的开销从8个字节增加到12个字节(对于字符串的实际指针,可能还有4个字节).每17x2 = 34字节行额外增加16个字节,几乎增加了50%.但我看到超过200%的开销.150%的额外费用是多少?


成功!!感谢大家的建议.你们都让我思考.但是我必须给Jan Goyvaerts一个答案,因为他问:

你为什么使用TStringList?文件真的必须作为单独的行存储在内存中吗?

这使我得到了解决方案,而不是将24 MB文件作为140万行StringList加载,我可以将我的行分组到我的程序知道的自然组中.因此,这导致127,000行加载到字符串列表中.

现在每行平均190个字符而不是17个.每个StringList行的开销是相同的,但现在有更少的行.

当我将其应用于320 MB文件时,它不再耗尽内存,现在加载的RAM不到1 GB.(它只需要大约10秒钟加载,这是非常好的!)

将有一些额外的处理来解析分组的行,但在每个组的实时处理中不应该注意到它.

(如果你想知道,这是一个家谱程序,这可能是我需要的最后一步,它允许它在不到30秒的时间内在32位地址空间中加载大约一百万人的所有数据.所以我'我们仍然需要一个20秒的缓冲区才能将索引添加到数据中,以便显示和编辑数据.)



1> Jan Goyvaert..:

你亲自问我在这里回答你的问题.我不知道你看到如此高内存使用的确切原因,但你需要记住TStringList不仅仅是加载你的文件.这些步骤中的每一步都需要可能导致内存碎片的内存.TStringList需要将文件加载到内存中,将其从Ansi转换为Unicode,将每个行拆分为一个字符串,然后将这些行填充到将重新分配多次的数组中.

我的问题是你为什么使用TStringList?文件真的必须作为单独的行存储在内存中吗?你打算在内存中修改文件,还是只显示它的一部分?将文件作为一个大块存储在内存中并使用与所需部分匹配的正则表达式扫描整个文件将比存储单独的行更有效.

此外,必须将整个文件转换为Unicode吗?虽然您的应用程序是Unicode,但您的文件是Ansi.我的一般建议是尽快将Ansi输入转换为Unicode,因为这样做可以节省CPU周期.但是当你有320 MB的Ansi数据将作为Ansi数据保留时,内存消耗将成为瓶颈.尝试将文件保存为内存中的Ansi,并仅将您要显示的部分转换为用户Ansi.

如果320 MB文件不是您要从中提取某些信息的数据文件,而是要修改的数据集,请考虑将其转换为关系数据库,并让数据库引擎担心如何管理大量数据内存有限.



2> Jim McKeeth..:

如果您使用AnsiString制作原始记录怎么办?那立刻把它砍成两半?仅仅因为Delphi默认使用UnicodeString并不意味着你必须使用它.

另外,如果你确切地知道每个字符串的长度(在一个字符或两个字符内),那么使用短字符串甚至更多的字节可能会更好.

我很好奇是否有更好的方法来完成你想要做的事情.将320 MB的文本加载到内存中可能不是最佳解决方案,即使您可以将其降低到仅需要320 MB



3> mghie..:

我使用Delphi 2009并且文件是ANSI但是在读取时转换为Unicode,所以你可以说转换后的文本大小是48 MB.

对不起,但我根本不明白这一点.如果你需要你的程序是Unicode,肯定文件是"ANSI"(它必须有一些字符集,如WIN1252或ISO8859_1)是不对的.我首先将它转换为UTF8.如果文件不包含任何字符> = 128,它将不会改变一个东西(它甚至会是相同的大小),但你已经为将来做好了准备.

现在您可以将其加载到UTF8字符串中,这不会使内存消耗增加一倍.将屏幕上可以同时显示的少数字符串实时转换为Delphi Unicode字符串将会更慢,但考虑到内存占用较少,您的程序在具有少量(免费)的系统上的性能会更好记忆.

现在,如果您的程序仍然使用TStringList消耗太多内存,您可以在程序中始终使用TStrings甚至IStrings,并编写一个实现IStrings或继承TStrings的类,并且不会将所有行保留在内存中.想到的一些想法:

    将文件读入TMemoryStream,并维护指向行的第一个字符的指针数组.返回一个字符串很简单,你只需要在行的开头和下一个字符串的开头之间返回一个正确的字符串,并删除CR和NL.

    如果仍然消耗太多内存,请将TMemoryStream替换为TFileStream,并且不维护char指针数组,但会启动该行的文件偏移数组.

    您还可以将Windows API函数用于内存映射文件.这允许您使用内存地址而不是文件偏移量,但不会消耗那么多内存作为第一个想法.

推荐阅读
雯颜哥_135
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有