Почему конструкторы, кажется, выделяют память для одного и того же объекта несколько раз? - PullRequest
0 голосов
/ 23 декабря 2011

Очевидно, я не понимаю, как работают конструкторы.

Когда мое приложение запускается, оно автоматически или при необходимости выполняет мою процедуру, которая загружает все ресурсы - в основном, изображения.

procedure Load;
begin
  AppPath := GetAppPath;
  INI := TInIFile.Create(AppPath + '\Config.ini');
  INI.WriteBool('Application', 'Running', True);
  ResPath := AppPath + '\Resources';
  Top := TPicture.Create;
  TopRight := TPicture.Create;
  Left := TPicture.Create;
  Right := TPicture.Create;
  BottomLeft := TPicture.Create;
  Bottom := TPicture.Create;
  BottomRight := TPicture.Create;

  ...

  //Load the pictures ...LoadFromFile(Skin.ReadString('Skin', ..., ...));

Пока я использовал это приложение, оно начало немного отставать, но ОС тоже зависала и зависала.Я был шокирован, когда увидел мою программу в диспетчере задач:

MEM

Если я прав, приложение использовало 600 мегабайт ОЗУ с включенным файлом подкачки.В моей системе всего 1 ГБ ОЗУ, поэтому неудивительно, что ОС запускалась с задержкой из-за использования файла подкачки.

Оказалось, что это была та же самая процедура, которая загружала ресурсы каждый раз, когда я что-то делал,Я исправил это, добавив логическое значение в модуль, и процедура была отменена, когда для него было установлено значение true (означает, что ресурсы загружены).

Loaded: Boolean;
...
if Loaded = False then Load;

Я не понимаю, зачем мне вообще нужно было сделать это изменение,Почему конструктор выделил память для уже созданных объектов?

1 Ответ

11 голосов
/ 23 декабря 2011

Конструкторы выделяют память только тогда, когда объект еще не существует. Если конструктор вызывается для существующего объекта, то этот объект создается; новый не выделяется. (То, что вы решите сделать внутри конструктора, это другой вопрос. Эти инструкции могут выделять больше памяти в дополнение к тому, что функция InstanceSize возвращает для класса.)

Ваш код не показывает конструкторов, вызываемых для существующих объектов. Вы вызываете конструкторы в классах , и это выделяет новый экземпляр каждого класса. Например, вы создаете объект INI-файла:

INI := TInIFile.Create(AppPath + '\Config.ini');

Это вызывает конструктор на TIniFile. Он выделяет новый экземпляр TIniFile и инициализирует его. Как только это закончится, ссылка на этот новый объект сохраняется в переменной INI. Важно понимать, что если INI уже содержит ссылку на некоторый другой объект, эта ссылка отбрасывается, а новая сохраняется вместо этого в INI. Вы не вызываете конструктор TIniFile для объекта, на который уже ссылается INI. Этот объект полностью независим от создаваемого нового.

Переменная INI не является объектом TIniFile. Это ссылка на объект.

Вы, очевидно, вызываете свою функцию Load несколько раз. Каждый раз, когда вы вызываете его, вы перезапускаете все конструкторы объектов, которые запускали ранее. У вас есть утечка памяти, потому что каждый раз, когда вы создаете новый набор объектов, вы сохраняете ссылки на них в тех же переменных, в которых вы уже сохранили предыдущие ссылки на объекты. Предыдущие значения отбрасываются, и нет никакого способа вернуть их обратно, поэтому нет способа освободить их. Способ решить эту проблему, как вы показали: убедитесь, что вы инициализируете программу только один раз, отслеживая, загружены ли вы уже.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...