我正在读取一个1.4百万行、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并且内存不足。
补充说明:Marco Cantu刚刚发布了关于Delphi和Unicode的白皮书。Delphi 2009将每个字符串的开销从8个字节增加到12个字节(加上实际指向字符串的指针可能还有4个字节)。每17x2 = 34个字节的额外16个字节几乎增加了50%。但我看到超过200%的开销。这多出来的150%是什么?
成功了!感谢大家的建议。你们让我思考了。但是我必须要给予Jan Goyvaerts答案的功劳,因为他问道:
...你为什么要使用TStringList?文件真的需要作为单独的行存储在内存中吗?
这引导我找到了解决方案,即将我的行分组成程序已知的自然组。因此,这导致127,000行加载到字符串列表中。
现在每行平均有190个字符而不是17个。每个StringList行的开销相同,但现在行数更少了。
当我将此应用于320MB文件时,它不再耗尽内存,现在只需要少于1GB的RAM即可加载。(而且只需要大约10秒钟才能加载,这非常好!)
解析分组行将需要一点额外的处理,但在每个组的实时处理中不应该会被注意到。
(如果你想知道,这是一个家谱程序,这可能是我需要的最后一步,允许它在不到30秒的32位地址空间中加载有关一百万人的所有数据。所以我还有20秒的缓冲时间来添加索引到数据中,这些索引将需要允许显示和编辑数据。)