最近几天,我在使用列表中的大量元素时看到了Delphi内存分配方面的一些奇怪的事情。
我的电脑有12 GB的内存;当我以64位模式运行我的应用程序(在列表中存储数据,高容量元素)时,我观察到我的计算机使用的内存缓慢上升10 GB,并在短时间内减少,直到使用率降至接近8 MB(正常在内存中启动时应用程序的大小)。 我想:如果我将数据存储在内存中,使用的内存大小应该不一样?因为为什么内存使用量从8 MB增加到10 GB并且回落到8 MB,当大量数据存储在内存中并且没有被清除时?
这是正常的吗?感谢您的任何建议。
更新
我报告完整的代码,可能会重现我以前告诉过的。
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
PsApi, windows, System.SysUtils, System.Generics.Collections;
type
TMyRecord = record
x: Integer;
y: Integer;
end;
TMyArr = array of TMyRecord;
TMyList = TList<TMyArr>;
function CurrentMemoryUsage: Cardinal;
var
pmc: TProcessMemoryCounters;
begin
pmc.cb := SizeOf(pmc);
if GetProcessMemoryInfo(GetCurrentProcess, @pmc, SizeOf(pmc)) then
Result := pmc.WorkingSetSize
else
RaiseLastOSError;
end;
var
MyArr: TMyArr;
MyList: TMyList;
iIndex1, iIndex2: Integer;
iMax: Int64;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
iMax := Low(Int64);
Writeln(FormatFloat('Memory used before to create list: ,.# K',
CurrentMemoryUsage / 1024));
MyList := TMyList.Create;
try
Writeln(FormatFloat('Memory used before load item in list: ,.# K',
CurrentMemoryUsage / 1024));
for iIndex1 := 0 to 20000000 do
begin
SetLength(MyArr, 100);
for iIndex2 := 0 to High(MyArr) do
with MyArr[iIndex2] do
begin
x := iIndex2 + 1;
y := iIndex2 - 1;
end;
MyList.Add(MyArr);
if CurrentMemoryUsage > iMax then
iMax := CurrentMemoryUsage;
end;
Writeln(FormatFloat
('Memory used (max) during to load item in list: ,.# K', iMax / 1024));
Writeln(FormatFloat('Memory used to end load item in list: ,.# K',
CurrentMemoryUsage / 1024));
finally
MyList.Free;
end;
Writeln(FormatFloat('Memory used after destroyed list: ,.# K',
CurrentMemoryUsage / 1024));
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
作为输出我有这样的东西:
Memory used before to create list: 3.452 K
Memory used before load item in list: 3.504 K
Memory used (max) during load item in list: 4.194.300 K
Memory used to end load item in list: 2.789.020 K
Memory used after destroyed list: 2.976 K
在这种情况下,告诉“在列表中加载项目期间”我得到差不多4 GB,但之后,当我完成加载项目列表内存使用时几乎是2-3 GB。 我试图做很多模拟,这个值改变,不是一个常数,但一般都尊重这个值。 我再说一遍,对我来说并不重要。只是为了更多地了解分配内存。
答案 0 :(得分:3)
从您对评论的回答中,您将数百万条记录放入列表中。你没有指定列表的类型,但如果它是一个TList或一个后代,它会自动增加列表以适应。
在增加列表时,它会重新分配一个新的更大的数组并将旧列表添加到其中,如果要在紧密循环中添加数百万个条目,这将导致在达到容量时重新分配内部列表(至少对于TList)。
旧列表的内存未被释放/可重复使用,我相信这是您使用大量内存的原因。
如果你知道你需要的列表大小,你应该使用Capacity属性来预设数组,如果没有在你的循环中加入一些额外的代码来跟踪列表的当前计数,如果接近容量会增加容量比它自动做的25%要大得多。这将大大减少内存分配量,从而减少内存使用量(也可以提高性能)。
希望有所帮助。
<强>更新强> 修改后的答案删除错误的列表调整大小信息,如Kohi所述。
答案 1 :(得分:1)
@Dampsquid是对的,但TList后代不会以16个元素的固定增量增长,并且很长时间没有这样做(D3?)。一旦你获得了64个元素,它在每次调整大小时都会增长25%。除非您使用免费版本的Delphi,否则您可以自己看到这个。按住Ctrl键并单击TList声明并查看。 Add()调用Grow(),如下所示:
procedure TList.Grow;
var
Delta: Integer;
begin
if FCapacity > 64 then
Delta := FCapacity div 4
else
if FCapacity > 8 then
Delta := 16
else
Delta := 4;
SetCapacity(FCapacity + Delta);
end;
即使对最终容量进行猜测,你仍然会好得多。如果您知道将要使用数百万条记录,请先将容量设置为一百万。这样你就可以跳过将列表增加到百万的所需的30多个调整大小操作。我会想要以另一种方式犯错,并猜测你认为你需要的最大数字。你每个额外的条目“浪费”8个字节,但避免了一堆重新分配和复制操作,所以错误的几百万只花费你16MB。当你使用GB的内存时什么都没有。
(抱歉答案,我没有足够的评论意见)。