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

Динамические массивы подсчитываются по ссылке, поэтому память автоматически освобождается компилятором. Мой вопрос: когда именно происходит автоматическое освобождение? Это происходит немедленно или в конце содержащейся процедуры?

Вот конкретный пример

procedure DoStuff;
var data:TBytes;
begin
  data:=GetData; // lets say data now contains 1 Gig of data.
  DoStuffWithData(data);
  // I now want to free up this 1Gig of memory before continuing.
  // Is this call needed, or would the memory be freed in the next line anyway?
  Finalize(data); 

  data:=GetMoreData; // The first array now has no remaining references
  DoStuffWithData(data);
end

Является ли вызов finalize() избыточным?

Ответ 1

Вызов Finalize не является излишним. Это правда, что счетчик ссылок динамического массива будет уменьшаться на следующей строке (поэтому, вероятно, уничтожает массив), но это произойдет только после выделения нового динамического массива. Как раз перед возвратом GetMoreData, но до выполнения назначения в памяти будет два динамических массива. Если вы сначала уничтожаете первый вручную, тогда вы будете иметь только один массив в памяти за раз.

Второй массив, который вы храните в data, будет уничтожен при возврате DoStuff (предполагается, что DoStuffWithData не хранит копию ссылки динамического массива в другом месте, увеличивая ее количество ссылок).

Ответ 2

Когда именно происходит автоматическое освобождение? Это происходит немедленно или в конце содержащейся процедуры?

Динамическая память, связанная с управляемыми типами (динамические массивы попадают в этот класс), освобождается, когда счетчик ссылок установлен в 0. Это может произойти в следующих пунктах:

  • Контрольной переменной присваивается новое значение. Вызов Finalize можно рассматривать как частный случай, когда новые значения равны нулю.
  • Контрольная переменная выходит за пределы области видимости. Например:
    • Достигнут выход функции; локальные переменные выходят за рамки.
    • Объект уничтожается, а его члены выходят за рамки.
    • Указатель на запись уничтожается с помощью функции Dispose; все поля записи выходят за рамки.
    • Финализируется блок, и все глобальные переменные, определенные в модуле, завершаются.

Обратите внимание, что вышеизложенные выше случаи приводят только к освобождению памяти, когда ссылка, которая завершается или покидает область видимости, является последней оставшейся ссылкой. Другими словами, когда счетчик ссылок равен 1.

В вашем конкретном примере, если исключить Finalize, вы создаете новый динамический массив и назначаете его переменной, которая уже содержит динамический массив. Затем это относится к классу, описанному в пункте 1 в списке выше. Таким образом, в этом смысле вызов Finalize является излишним.

Роб объяснил порядок, в котором происходит распределение и освобождение, что является хорошей точкой. Насколько я знаю, это деталь реализации, которая явно не документирована. Однако я был бы поражен, если бы эта деталь была изменена.