Какие бухгалтерские данные содержит динамический массив Delphi?

Вот простая программа для проверки распределения памяти. Проверка значений до и после с помощью диспетчера задач показывает, что каждый динамический массив занимает 20 байт памяти при размере = 1. Размер элемента равен 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.

person Mason Wheeler    schedule 14.12.2009    source источник


Ответы (3)


Я также просмотрел System.pas и заметил, что вызов GetMem в _DynArrayCopyRange поддерживает ваш анализ:

выделенный размер = количество * размер элемента + 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.

Это дает

  An unexpected memory leak has occurred. The unexpected small block leaks are:

  1 - 12 bytes: Unknown x 1
  13 - 20 bytes: Unknown x 2
  21 - 28 bytes: Unknown x 2
  29 - 36 bytes: Unknown x 1

который поддерживает теорию 8-байтовых служебных данных.

person Uli Gerhardt    schedule 14.12.2009
comment
Ты прав. Это прямо в коде. И диспетчер задач довольно точен, но он измеряет объем оперативной памяти, выделенной приложению из Windows. Итак, реальный вопрос заключается в том, почему FastMM захватывает у ОС гораздо больше памяти, чем ему нужно? Может быть, чтобы уменьшить общее количество требуемых запросов памяти и снизить фрагментацию? - person Mason Wheeler; 15.12.2009
comment
Диспетчер задач НЕ точен при диагностике использования памяти Delphi. Он не принимает во внимание дополнительные накладные расходы, которые необходимы диспетчеру памяти VCL для его собственных внутренних компонентов в дополнение к накладным расходам самого динамического массива. И не учитывается, что диспетчер памяти кэширует и повторно использует блоки памяти при их освобождении, так как они не возвращаются в ОС. - person Remy Lebeau; 15.12.2009
comment
да. Я имел в виду, что диспетчер задач точно измеряет то, что он измеряет, а именно то, сколько приложение запросило у Windows, а не обязательно то, сколько приложение фактически использует внутри. - person Mason Wheeler; 15.12.2009
comment
Насколько я помню, FastMM использует умный подход. Он не возвращает каждый блок освобожденной памяти обратно сразу. Это разумно, потому что обычно следуют новые распределения. Вероятно, он имеет аналогичный подход к размещению массива. Но я этого точно не знаю. - person Runner; 15.12.2009

Выделения памяти имеют гранулярность, чтобы гарантировать, что все выделения выровнены. Это просто отстой, вызванный этим.

person Loren Pechtel    schedule 15.12.2009

Обновлено... На самом деле я пошел проверить код (что должен был сделать раньше) и пришел к тому же выводу, что и Ульрих, он не хранит никакой информации о типе, только служебные данные 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;
person Francesca    schedule 14.12.2009
comment
Я так не думаю. Кажется, что все соответствующие подпрограммы принимают информацию о типе как отдельный параметр, который отслеживает компилятор. - person Mason Wheeler; 15.12.2009
comment
Re: нелинейность: я думаю, это артефакт диспетчера памяти и ничего особенного в массивах dyn. - person Uli Gerhardt; 15.12.2009
comment
У меня были проблемы с выделением памяти с использованием динамических массивов и SetLength, которые возникают, если вы создаете расширяющийся список (теперь очевидно). Я не хотел использовать потомка TList. Простым способом обойти это было бы иметь параметр «емкость» для динамических массивов, я подумал о том, чтобы сделать свой собственный GetMem, а затем использовать ABSOLUTE, чтобы исправить расширяющийся массив в том же «слоте», но yuk..... I' Я подозревал, что компилятор не может поддерживать эту функцию. - person Brian Frost; 16.12.2009