为什么不能 Tstringlist.LoadFromfile 加载合理大小的文件?
Why can't Tstringlist.LoadFromfile load a reasonably sized file?
32位8GB内存Win 10系统编译
尝试使用 Tstringlist.Loadfromfile 加载一个 150Mb 的 ASCII 文件给出 "Out of Memory Error",任务管理器报告已使用 1200Mb。
即使有 50% 的 Unicode 冗余也无法解释这种低效率!
知道发生了什么事吗?
示例代码。
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm3 = class(TForm)
OpenDialog1: TOpenDialog;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure TForm3.Button1Click(Sender: TObject);
var f:tstringlist;
begin
if opendialog1.Execute then
begin
f:=Tstringlist.create;
try
f.Loadfromfile(Opendialog1.Filename);
finally
f.free
end
end;
end;
end.
根据要求,下面文件中的一些代表性文本....
AcDbPolyline
90
5
70
0
10
100091.01
20
59019.75
10
100077.39
20
59001.49
10
100070.7
20
58974.72
10
100066.85
20
58942.73
10
100065.12
20
58920.69
0
LWPOLYLINE
5
Notepad++ 报告文件有 2700 万行。
每个字符串都需要单独的堆分配。每个堆分配都会产生一块内存,但也会产生一些由内存管理器使用的元数据。最重要的是,字符串有自己的元数据、引用计数和长度。
如果您的文件包含很多非常短的行,那么元数据很容易占据主导地位。在极端情况下,单个字符串很容易消耗 20 个字节或更多。在我的脑海中,我不知道实际的开销数字,所以这是一个猜测。
非常短的行会有很多字符串。字符串列表拥有的指针数组本身可能非常大。如果通过调整内存大小进行分配,可能会导致碎片化。你说有2700万行。每行两个指针,一个是字符串,一个是它的关联对象,仅此一项就超过 200MB。
总而言之,TStringList
是您任务的错误类型。您最好使用字符串 reader 对象逐行读取文件。或者将整个文件加载到内存中,并使用专用类型将行映射到偏移量,就像文本编辑器所做的那样。事实上,文本编辑器通常使用内存映射来避免地址 space 危机。在不知道您想对文件做什么的情况下,很难提出建议,但我希望我能帮助您理解为什么您当前的方法没有前途。
32位8GB内存Win 10系统编译
尝试使用 Tstringlist.Loadfromfile 加载一个 150Mb 的 ASCII 文件给出 "Out of Memory Error",任务管理器报告已使用 1200Mb。
即使有 50% 的 Unicode 冗余也无法解释这种低效率!
知道发生了什么事吗?
示例代码。
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm3 = class(TForm)
OpenDialog1: TOpenDialog;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure TForm3.Button1Click(Sender: TObject);
var f:tstringlist;
begin
if opendialog1.Execute then
begin
f:=Tstringlist.create;
try
f.Loadfromfile(Opendialog1.Filename);
finally
f.free
end
end;
end;
end.
根据要求,下面文件中的一些代表性文本....
AcDbPolyline
90
5
70
0
10
100091.01
20
59019.75
10
100077.39
20
59001.49
10
100070.7
20
58974.72
10
100066.85
20
58942.73
10
100065.12
20
58920.69
0
LWPOLYLINE
5
Notepad++ 报告文件有 2700 万行。
每个字符串都需要单独的堆分配。每个堆分配都会产生一块内存,但也会产生一些由内存管理器使用的元数据。最重要的是,字符串有自己的元数据、引用计数和长度。
如果您的文件包含很多非常短的行,那么元数据很容易占据主导地位。在极端情况下,单个字符串很容易消耗 20 个字节或更多。在我的脑海中,我不知道实际的开销数字,所以这是一个猜测。
非常短的行会有很多字符串。字符串列表拥有的指针数组本身可能非常大。如果通过调整内存大小进行分配,可能会导致碎片化。你说有2700万行。每行两个指针,一个是字符串,一个是它的关联对象,仅此一项就超过 200MB。
总而言之,TStringList
是您任务的错误类型。您最好使用字符串 reader 对象逐行读取文件。或者将整个文件加载到内存中,并使用专用类型将行映射到偏移量,就像文本编辑器所做的那样。事实上,文本编辑器通常使用内存映射来避免地址 space 危机。在不知道您想对文件做什么的情况下,很难提出建议,但我希望我能帮助您理解为什么您当前的方法没有前途。