4

这是一个检查内存分配的简单程序。使用任务管理器检查前后值表明每个动态数组在 size = 1 时占用 20 个字节的内存。元素大小为 4,这意味着簿记数据的开销为 16 个字节。

通过查看 system.pas,我可以找到一个 -4 字节的数组长度字段和一个 -8 字节的引用计数,但我似乎找不到对其他 8 个字节的任何引用。有人知道他们在做什么吗?

示例程序:

program Project1;

{$APPTYPE CONSOLE}

type
   TDynArray = array of integer;
   TLotsOfArrays = array[1..1000000] of TDynArray;
   PLotsOfArrays = ^TLotsOfArrays;

procedure allocateArrays;
var
   arrays: PLotsOfArrays;
   i: integer;
begin
   new(arrays);
   for I := 1 to 1000000 do
      setLength(arrays^[i], 1);
end;

begin
  readln;
  allocateArrays;
  readln;
end.
4

3 回答 3

5

我也查看了 System.pas 并注意到 _DynArrayCopyRange 中的 GetMem 调用支持您的分析:

分配大小 = 计数 * 元素大小 + 2 * Sizeof(Longint)

. 因此,您从任务管理器获得的数字可能不是很准确。您可以尝试Pointer(someDynArray) := nil检查 FastMM 报告的内存泄漏大小以获得更可靠的数字。

编辑:我做了一个小测试程序:

program DynArrayLeak;

{$APPTYPE CONSOLE}

uses
  SysUtils;

procedure Test;
var
  arr: array of Integer;
  i: Integer;
begin
  for i := 1 to 6 do
  begin
    SetLength(arr, i);
    Pointer(arr) := nil;
  end;
end;

begin
  ReportMemoryLeaksOnShutdown := True;
  Test;
end.

这产生

  发生了意外的内存泄漏。意外的小块泄漏是:

  1 - 12 字节:未知 x 1
  13 - 20 字节:未知 x 2
  21 - 28 字节:未知 x 2
  29 - 36 字节:未知 x 1

支持8字节开销理论。

于 2009-12-14T23:04:20.070 回答
2

内存分配具有确保所有分配对齐的粒度。这只是由此造成的败笔。

于 2009-12-15T01:25:52.297 回答
0

更新了...我实际上去检查了代码(我以前应该这样做过),我得出了与 Ulrich 相同的结论,它没有存储任何类型信息,只是 2 个 Longint 开销,然后是 NbElements*ElementSize。
而且,任务管理器对于这种措施并不准确。

奇怪的是,如果您测量 dynarray 使用的内存,它会随着元素的大小非线性增加:对于具有 2 或 3 个整数的记录,它的大小相同(20),而 4 或 5 则为 28...遵循块大小的粒度。

内存测量:

// Return the total Memory used as reported by the Memory Manager
function MemoryUsed: Cardinal;
var
  MemMgrState: TMemoryManagerState;
  SmallBlockState: TSmallBlockTypeState;
begin
  GetMemoryManagerState(MemMgrState);
  Result := MemMgrState.TotalAllocatedMediumBlockSize + MemMgrState.TotalAllocatedLargeBlockSize;
  for SmallBlockState in MemMgrState.SmallBlockTypeStates do begin
      Result := Result + SmallBlockState.UseableBlockSize * SmallBlockState.AllocatedBlockCount;
  end;
end;
于 2009-12-14T22:23:24.957 回答