Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Утечка памяти  (Прочитано 16746 раз)
0 Пользователей и 1 Гость смотрят эту тему.
SCRIBE
Гость
« : 06-05-2016 07:48 » 

Привет, друзья.

Почитал я гугл, чет никак.
Проблема только при обработке сообщения ОС.
Есть такое сообщение как WM_WINDOWPOSCHANGED, срабатывает при изменении размеров окна.
Хочу собственно сохранить эти значения для удобства пользователя.
Включим отображение утечек: ReportMemoryLeaksOnShutdown := true;
Обработка сообщения:
Код: (Delphi)
procedure WMWindowPosChanged(var aMessage: TWMWindowPosChanged); message WM_WINDOWPOSCHANGED;

procedure TfmMain.WMWindowPosChanged(var aMessage: TWMWindowPosChanged);
begin
  inherited;
  SaveFormSize(fmMain.Width, fmMain.Height); // метод формы. на котором собственно и возникает утечка
end;

procedure TfmMain.SaveFormSize(const aWidth, aHeight: integer);
begin
  if Assigned(AppOptions) then
  begin
    AppOptions.SetOption('GUI', 'FormWidth', inttostr(aWidth)); // Передаются в метод как const
    AppOptions.SetOption('GUI', 'FormHeight', inttostr(aHeight)); // Эти самые строки, что не освобождаются, 4шт.
  end;
end;

TOption = packed record
    Section: string;
    Name: string;
    Value: string;

TOptions = class
  private
    FOptions: array of TOption;


function TOptions.SetOption(const aSection, aName, aValue: string): boolean;
var
  i: integer;
  Founded: boolean;
begin
  Result:= False;
  Founded:= false;
  if (aSection = '') or (aName = '') or (aValue = '') then
    raise Exception.Create('Some of option agrument is empty!');
  for i := 0 to high(FOptions) do
  begin
    if (FOptions[i].Section = aSection) and (FOptions[i].Name = aName) then
    begin
      FOptions[i].Value:= aValue;
      Founded:= true;
    end;
  end;
  if not Founded then
  begin
    SetLength(FOptions, length(FOptions) + 1);
    FOptions[high(FOptions)].Section:= aSection;
    FOptions[high(FOptions)].Name:= aName;
    FOptions[high(FOptions)].Value:= aValue;
  end;
  FLastChange:= GetTime;
  FEmpty:= false;
  Result:= true;
end;

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

Записан
zubr
Гость
« Ответ #1 : 06-05-2016 08:24 » 

SetLength(FOptions...  память выделяется, а где она потом удаляется?
Записан
SCRIBE
Гость
« Ответ #2 : 06-05-2016 08:25 » 

Код: (Delphi)
destructor TOptions.Destroy;
begin
  SetLength(FOptions, 0);
  inherited Destroy;
end;
Записан
SCRIBE
Гость
« Ответ #3 : 06-05-2016 08:37 » 

Даже так пробовал.

Код: (Delphi)
TOption = packed record
    Section: string;
    Name: string;
    Value: string;
    Procedure Clear;
  end;

procedure TOption.Clear;
begin
  Self:= Default(TOption);
end;

destructor TOptions.Destroy;
var
  i: integer;
begin
  for i := 0 to high(FOptions) do
    FOptions[i].Clear;
  SetLength(FOptions, 0);
  inherited Destroy;
end;
Записан
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #4 : 22-06-2016 00:08 » 

в дельфях работа с памятью люто централизована. всеми выделениями и освобождениями памяти занимается только менеджер памяти. с 2007, если не ошибаюсь, его роль выполняет FastMM. и факт утечки константируется на основе того простого факта, что кол-во выделений памяти неравно кол-ву освобождений памяти. больше ReportMemoryLeakOnShutdown ничего не даст.

и если оно выдает ошибку на методе, это означает только, что внутри метода инициализировано больше переменных, чем освобождено. и это логично, если вы хотите внутри метода созранить какую-то инфу в каком-то объекте, так, чтобы он был доступен вне этого метода, зачем вы создаете этот объект внутри метода? создайте и инициализируйте его, например, в FormCreate, освобождайте в FormDestroy, в внутри метода просто заполняйте нужными значениями уже выделенные в памяти ячейки массива. и FastMM будет спокоен, аки испанская балерина под галаперидолом.

З.Ы. в сабж: http://www.gunsmoker.ru/2009/05/blog-post_24.html
« Последнее редактирование: 22-06-2016 00:09 от x77 » Записан

SCRIBE
Гость
« Ответ #5 : 22-06-2016 08:56 » 

Прочитал и все равно не понимаю в чем проблема. Да, я создал объект (между прочим было без разницы, packed record там или class) в методе, что тут такого, я его и освободил при выходе из программы деструктором, как порядочный гражданин прошелся по массиву. В других модулях также используются динамические массивы с объектами, и никаких проблем.

В любом случае, пришлось просто отказаться вызывать его в обработчике события WM_WINDOWPOSCHANGED, а по старинке при закрытии программы. Утечка пропала.

И спасибо за ссыль.
Записан
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #6 : 22-06-2016 16:47 » 

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

насчет packed record. по дефолту записи выравниваются по границе в 8 байт. что увеличивает скорость доступа к ним. размер string в зависимости от версии дельфи может "плавать" от 256 байт до нескольких гигабайт, поэтому попытки "паковать" структуры с этим типом - они какбе сомнительны Ага при использовании packed record строки лучше жестко типизовать оббявляя заранее их рамер, string [10], например, только тогда это будет иметь какой-то смысл.
« Последнее редактирование: 22-06-2016 17:05 от x77 » Записан

SCRIBE
Гость
« Ответ #7 : 29-06-2016 19:30 » 

метод вызывается кучу раз, когда окно меняет размер или перемещается, и как он может создастся дважды,  будет просто новый элемент массива, который потом освобождается. и оно там проверяет, нет ли в массиве объектов с таким именем параметра, если есть, меняет в нем значение, иначе новый элемент массива, все как бы)
Записан
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #8 : 02-07-2016 22:43 » 

ну судя по коду (я так понял, тут не весь код), память выделяется многократно (по кол-ву вызовов метода), а освобождается только разово. утилиты контроля памяти не делают ничего сверхЪестественного, они просто переопределяют системные методы резервирования и освобождения памяти. вставляя туда свои собственные счетчики выделений и освобождений. дельфийский менеджер памяти - это не черный ящик, вы запросто можете написать свой и назначить его вместо. и когда память выделялась больше раз, чем освобождалась - они рапортуют о memory leak, вот и все.

попробуйте задавать массив при инициализации приложения, SetLength - это тоже выделение памяти.

не видя всего кода - это гадание на кофейной гуще, поэтому извините, конечно, может я просто что-то не догоняю Улыбаюсь
Записан

SCRIBE
Гость
« Ответ #9 : 19-07-2016 06:25 » 

Весь код этого модуля тут https://github.com/justscribe/ORDESY/blob/master/uOptions.pas,
как и весь проект https://github.com/justscribe/ORDESY
Записан
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #10 : 01-08-2016 21:05 » 

сорри, я тут набегами, и нотификация почему-то была отключена. если ты не против, я поковыряю код, не обещаю правда, что быстро )
Записан

v2
Помогающий

ua
Offline Offline

« Ответ #11 : 16-09-2016 16:07 » new

Код: (Delphi)
  Application.Run;
end.
  и вроде энд, но тут прилетает хеппи WM_WINDOWPOSCHANGED ))


Код: (Delphi)
procedure TfmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Tag := -1;                                    // можно так скостылить
  FreeApp(Action);
end;
//+
procedure TfmMain.WMWindowPosChanged(var aMessage: TWMWindowPosChanged);
begin
 if Tag=0 then begin                            // ...
  SaveFormSize(fmMain.Width, fmMain.Height);
  inherited;
 end;
end;


я не поклонник FreeAndNil, но если б он там был ... ))

Код: (Delphi)
procedure TfmMain.FreeApp(var Action: TCloseAction);
...
      if Assigned(AppOptions) then                                        
      begin
        AppOptions.SetOption('GUI', 'GroupList', IntToStr(tvMain.Width));
        AppOptions.SetOption('GUI', 'FormLeft', inttostr(fmMain.Left));
        AppOptions.SetOption('GUI', 'FormTop', inttostr(fmMain.Top));
        SaveFormSize(fmMain.Width, fmMain.Height);
        if not AppOptions.SaveUserOptions() then
          raise Exception.Create('Cant''t save user options!');                                                                  // после нас хоть потоп - а, ну да, на то оно и 'raise'  )))
        AppOptions.Free;                                           // может FreeAndNil ?
      end;
...

procedure TfmMain.SaveFormSize(const aWidth, aHeight: integer);
begin
  if Assigned(AppOptions) then                                     // да, FreeAndNil !!!
  begin
    AppOptions.SetOption('GUI', 'FormWidth', inttostr(aWidth));
    AppOptions.SetOption('GUI', 'FormHeight', inttostr(aHeight));
  end;
end;


и еще (что заметилось):

Код: (Delphi)
function TOptions.SetOption(const aSection, aName, aValue: string): boolean;
...
    for i := 0 to high(FOptions) do
    begin
      if (FOptions[i].Section = aSection) and (FOptions[i].Name = aName) then
      begin
        FOptions[i].Value:= aValue;
        Founded:= true;
        Break;                                  // не помешает
      end;
    end;
...






Записан
SCRIBE
Гость
« Ответ #12 : 17-09-2016 17:41 » 

Насчет Assigned, да, обманчив он, чисто проверка на Nil, работать должен с FreeAndNil.

Вот значит как, объект с настройками освободился, а виндовс пытается в него запихнуть значения...
Спасибо огромное человек, флаг на начало закрытия формы уже можно сообразить.
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines