我正在阅读一个包含 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 并且由于这个多余的字符串要求而耗尽内存。
附录: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 文件时,它不再耗尽内存,现在加载不到 1 GB 的 RAM。(而且加载只需要大约 10 秒,相当不错!)
解析分组的行会有一点额外的处理,但在每个组的实时处理中应该不明显。
(如果您想知道,这是一个家谱程序,这可能是我需要让它在不到 30 秒的时间内将大约 100 万人的所有数据加载到 32 位地址空间中的最后一步。所以我我们仍然有 20 秒的缓冲时间来将索引添加到数据中,这将需要允许显示和编辑数据。)