高容量元素和列表以及“奇怪”的分配内存

时间:2012-03-04 18:53:16

标签: delphi memory-management delphi-xe2

最近几天,我在使用列表中的大量元素时看到了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。 我试图做很多模拟,这个值改变,不是一个常数,但一般都尊重这个值。 我再说一遍,对我来说并不重要。只是为了更多地了解分配内存。

2 个答案:

答案 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的内存时什么都没有。

(抱歉答案,我没有足够的评论意见)。