Не освобождается память при уничтожении компонентов

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Ответить
Аватара пользователя
rayanAyar
новенький
Сообщения: 42
Зарегистрирован: 06.01.2011 07:22:52
Откуда: Новоуральск

Не освобождается память при уничтожении компонентов

Сообщение rayanAyar »

Мне нужно в runtime создавать и уничтожать компоненты. Иногда в большом количестве. Проблема в том, что после уничтожения компонентов занимаемая ими память уже не освобождается.

Может быть я не правильно создаю/уничтожаю компоненты?

Или это особенности работы менеджера памяти FPC ? Если это менеджер памяти такой - может быть есть способ запустить "сборку мусора"?

В приведенном ниже примере:
- запуск приложения: 3472kB
- после создания компонентов: 10220kB
- после уничтожения компонентов: 9708kB

Как вернуть системе эти 6 мегабайт?

Код: Выделить всё

unit Unit1; 

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton; //Показать использование памяти
    Button2: TButton; //Создать компоненты
    Button3: TButton; //Уничтожить компоненты
    Label1: TLabel;
    ScrollBox1: TScrollBox;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  sl: TStringList;
begin
  {$IFDEF UNIX}
  sl:= TStringList.Create;
  sl.LoadFromFile('/proc/self/status');
  sl.NameValueSeparator:= ':';
  Label1.Caption:= Trim(sl.Values['VmData']);
  sl.Free;
  sl:= nil;
  {$ENDIF}
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  ctrlChild: TEdit;
  lCntr: Longint;
begin
  ScrollBox1.AutoScroll:= False;
  for lCntr:= 1 to 1000 do
  begin
    ctrlChild:= TEdit.Create(ScrollBox1);
    ctrlChild.Name:= 'ctrl' + IntToStr(lCntr);
    ctrlChild.Top:= ctrlChild.Height * (lCntr - 1);
    ctrlChild.Text:= 'ctrl' + IntToStr(lCntr);
    ctrlChild.Parent:= ScrollBox1;
  end;
  ScrollBox1.AutoScroll:= True;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  lCntr: Longint;
begin
  ScrollBox1.AutoScroll:= False;
  for lCntr:= ScrollBox1.ComponentCount downto 1 do
  begin
    ScrollBox1.Components[lCntr - 1].Free;
  end;
  ScrollBox1.AutoScroll:= True;
end;

end.


Пробовал на Lazarus 0.9.30 GTK2, Debian/Ubuntu i386/amd64.
Аватара пользователя
VirtUX
энтузиаст
Сообщения: 880
Зарегистрирован: 05.02.2008 09:52:19
Откуда: Крым, Алушта

Сообщение VirtUX »

А если вместо

Код: Выделить всё

ScrollBox1.Components[lCntr - 1].Free;
написать

Код: Выделить всё

FreeAndNil(ScrollBox1.Components[lCntr - 1]);
?
Хотя здесь вряд-ли что-то изменится...
sign
энтузиаст
Сообщения: 1131
Зарегистрирован: 30.08.2009 09:20:53

Сообщение sign »

windows XP.
Lazarus 0.9.30
FPC 2.4.2

Код: Выделить всё

Запуск: 8 164

Button2: 10 156   10 196   10 168   10 216   10 408   10 436
Button3:  9 372    9 412    9 408    9 484    9 480    4 476
Аватара пользователя
rayanAyar
новенький
Сообщения: 42
Зарегистрирован: 06.01.2011 07:22:52
Откуда: Новоуральск

Сообщение rayanAyar »

VirtUX писал(а):А если вместо

Код: Выделить всё

ScrollBox1.Components[lCntr - 1].Free;
написать

Код: Выделить всё

FreeAndNil(ScrollBox1.Components[lCntr - 1]);
?
Хотя здесь вряд-ли что-то изменится...

Пробовал - никакой разницы. Собственно и не должно её быть. FreeAndNil - это вызов Free и присваивание nil.

sign писал(а):

Код: Выделить всё

Запуск: 8 164

Button2: 10 156   10 196   10 168   10 216   10 408   10 436
Button3:  9 372    9 412    9 408    9 484    9 480    4 476

Круто :). "Миллиард китайцев попробовали пароль 12345 на доступ к сайту pentagon.gov, на половине попыток компьютер пентагона согласился."

А как вобще могло получиться меньше чем при старте?
Эти результаты повторяемые?
sign
энтузиаст
Сообщения: 1131
Зарегистрирован: 30.08.2009 09:20:53

Сообщение sign »

В последней колонке опечатка, читать - 9 476
Odyssey
энтузиаст
Сообщения: 580
Зарегистрирован: 29.11.2007 16:32:24

Сообщение Odyssey »

Чтобы найти утечку памяти, если она есть, можно воспользоваться heaptrc:

Код: Выделить всё

uses HeapTrc, ... // модуль должен быть первым в секции uses
...
begin
  SetHeapTraceOutput('heaptrc_log.txt');
  ...
end.

И после завершения программы в файле можно будет посмотреть список утечек.
Аватара пользователя
rayanAyar
новенький
Сообщения: 42
Зарегистрирован: 06.01.2011 07:22:52
Откуда: Новоуральск

Сообщение rayanAyar »

Код: Выделить всё

Heap dump by heaptrc unit
38862 memory blocks allocated : 5033375/5114272
38862 memory blocks freed     : 5033375/5114272
0 unfreed memory blocks : 0
True heap size : 393216
True free heap : 393984
Should be : 393216

HeapTrc говорит, что "0 unfreed".
.wOvAN
постоялец
Сообщения: 118
Зарегистрирован: 16.04.2010 06:36:12
Контактная информация:

Сообщение .wOvAN »

Нажмите свернуть окно, затем обратно разверните. :)
Аватара пользователя
rayanAyar
новенький
Сообщения: 42
Зарегистрирован: 06.01.2011 07:22:52
Откуда: Новоуральск

Сообщение rayanAyar »

Никакой реакции на сворачивание/разворачивание.
Mr.Smart
долгожитель
Сообщения: 1796
Зарегистрирован: 29.03.2008 00:01:11
Откуда: из леса!

Сообщение Mr.Smart »

.wOvAN Это в Виндас.

Добавлено спустя 1 минуту 35 секунд:
rayanAyar если так напрягает, что стандартный мнеджер памяти FPC не сразу отдаёт память (это кстати сделано для увеличения производительности) используйте другой, например cmem.
Аватара пользователя
rayanAyar
новенький
Сообщения: 42
Зарегистрирован: 06.01.2011 07:22:52
Откуда: Новоуральск

Сообщение rayanAyar »

Mr.Smart писал(а):rayanAyar если так напрягает, что стандартный мнеджер памяти FPC не сразу отдаёт память (это кстати сделано для увеличения производительности)

Что значит "не сразу"? Через какое время он вернет память системе?
Меня бы это не напрягало, если бы со временем память возвращалась. Но она не освобождается. Даже через несколько часов. Может быть есть какая-то возможность управлять менеджером памяти?


Mr.Smart писал(а):rayanAyar используйте другой, например cmem.

Пробовал - такая же ситуация.
Odyssey
энтузиаст
Сообщения: 580
Зарегистрирован: 29.11.2007 16:32:24

Сообщение Odyssey »

rayanAyar писал(а):Может быть есть какая-то возможность управлять менеджером памяти?

Точно можно создать свой менеджер. Насчёт управления существующим, наверное, смогут ответить только разработчики в рассылке.
Mr.Smart
долгожитель
Сообщения: 1796
Зарегистрирован: 29.03.2008 00:01:11
Откуда: из леса!

Сообщение Mr.Smart »

rayanAyar писал(а):
Mr.Smart писал(а):rayanAyar используйте другой, например cmem.

Пробовал - такая же ситуация.

cmem использует непосредственно функции выделения и освобождение памяти из библиотеке libc и сам распределением блоков не занимается. Т.ч. возможно ошибки у вас в коде.
Как вы определяете какой объём памяти занимает в данный момент ваша программа?
Аватара пользователя
rayanAyar
новенький
Сообщения: 42
Зарегистрирован: 06.01.2011 07:22:52
Откуда: Новоуральск

Сообщение rayanAyar »

Mr.Smart писал(а):Как вы определяете какой объём памяти занимает в данный момент ваша программа?

Стандартными утилитами: htop, ps, Системный монитор. Все эти утилиты берут информацию об использовании памяти из /proc. От туда же я читаю эту информацию в программе.

Самое интересное, что после уничтожения всех компонентов heap освобождается до исходного состояния. Но при этом "система говорит", что сегмент данных приложения гораздо больше heap.

Вот например:
запуск приложения
CurrHeapSize: 2260992; CurrHeapUsed: 561728; CurrHeapFree: 1699264; RSS: 11724 kB; DataSegment: 3588 kB;
создание 5000 компонентов (Button2)
CurrHeapSize: 9011200; CurrHeapUsed: 8091744; CurrHeapFree: 919456; RSS: 49620 kB; DataSegment: 40812 kB;
уничтожение всех созданных компонентов (Button3)
CurrHeapSize: 2654208; CurrHeapUsed: 560352; CurrHeapFree: 2093856; RSS: 43240 kB; DataSegment: 34216 kB;

Итого 30МБ приложение не вернуло системе.

Код: Выделить всё

unit Unit1; 

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ExtCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton; //Показать использование памяти
    Button2: TButton; //Создать компоненты
    Button3: TButton; //Уничтожить компоненты
    Edit1: TEdit; //Количество создаваемых компонентов
    Memo1: TMemo;
    ScrollBox1: TScrollBox;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  slProc: TStringList;
  FPCHeapStatus: TFPCHeapStatus;
  sTmp: String;
begin
  sTmp:= '';

  FPCHeapStatus:= GetFPCHeapStatus;
  sTmp:= sTmp + 'CurrHeapSize: ' + IntToStr(FPCHeapStatus.CurrHeapSize) + '; ';
  sTmp:= sTmp + 'CurrHeapUsed: ' + IntToStr(FPCHeapStatus.CurrHeapUsed) + '; ';
  sTmp:= sTmp + 'CurrHeapFree: ' + IntToStr(FPCHeapStatus.CurrHeapFree) + '; ';

  {$IFDEF UNIX}
  slProc:= TStringList.Create;
  slProc.LoadFromFile('/proc/self/status');
  slProc.NameValueSeparator:= ':';
  sTmp:= sTmp + 'RSS: ' + Trim(slProc.Values['VmRSS']) + '; ';
  sTmp:= sTmp + 'DataSegment: ' + Trim(slProc.Values['VmData']) + '; ';
  slProc.Free;
  slProc:= nil;
  {$ENDIF}

  Memo1.Lines.Add(sTmp);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  ctrlChild: TEdit;
  lCntr: Longint;
begin
  ScrollBox1.AutoScroll:= False;
  for lCntr:= 1 to StrToInt64Def(Edit1.Text, 1000) do
  begin
    ctrlChild:= TEdit.Create(ScrollBox1);
    ctrlChild.Name:= 'ctrl' + IntToStr(lCntr);
    ctrlChild.Top:= ctrlChild.Height * (lCntr - 1);
    ctrlChild.Text:= 'ctrl' + IntToStr(lCntr);
    ctrlChild.Parent:= ScrollBox1;
  end;
  ScrollBox1.AutoScroll:= True;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  ctrlChild: TEdit;
  lCntr: Longint;
begin
  ScrollBox1.AutoScroll:= False;
  for lCntr:= ScrollBox1.ComponentCount downto 1 do
  begin
    ctrlChild:= TEdit(ScrollBox1.Components[lCntr - 1]);
    ctrlChild.Free;
  end;
  ScrollBox1.AutoScroll:= True;
end;

end.
Ответить