Утечка памяти в программе

Вопросы программирования на Free Pascal, использования компилятора и утилит.

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

Утечка памяти в программе

Сообщение Rakshas » 13.02.2011 04:12:14

Прошу помощи. Пишу модель для вычислений в конечных полях. Для представления элемента поля использовал класс TGFElement, чтобы перегрузить операторы.
Расчет производится верно, но программа с огромной скоростью пожирает память (~ 3GB за 4 сек и потом лезет в SWAP).
Который день не могу отловить эту ошибку. valgrind показывает, что утечки происходят во время действий/операций с созданным классом. А где, в упор не понимаю.
Кому не сложно --- помогите, гляньте свежим глазом.

ОС: Ubuntu 10.10, FPC 2.4.0

Вот часть кода программы:
1. fpc-model.pas
Код: Выделить всё
program Model;

uses crt, mclasses, gfclasses, convert, sysutils;

BEGIN
    gf_stepen := 5;
    gf_polynom := '101001';
    SetGF;
    SetGFDBazis;
    // ...
    // Очищаем память
    GFFreeMem;
END.


2. gfclasses.pas
Код: Выделить всё
unit gfclasses;

{$mode objfpc}{$H+}
{$ASMMODE INTEL}

interface

uses convert, classes, sysutils;

type
   TGFElement = class
      private
         z: integer;      // Значение элемента (десятичное)
      public
         procedure zero;               // Обнуление элемента
         function ifnotzero: boolean;   // Если элемент не равен 0, то возвращает true
         function exponent: integer;      // Возвращает показатель степени элемента поля (!! ПОДУМАТЬ: что если значение равно нулю)
         //function value: integer;      // Возвращает значение элемента поля
         function print: string;
         function printtex: string;
         constructor Create;
         destructor Destroy; override;
   end;
   operator +(const a,b: TGFElement)r: TGFElement;
   operator *(const a,b: TGFElement)r: TGFElement;
   operator /(const a,b: TGFElement)r: TGFElement;
   operator **(const a: TGFElement; const b: integer)r: TGFElement;   // Возведение в степень
   operator :=(a: integer)r: TGFElement;

// П/п вычисления поля Галуа по заданным параметрам (степень и образ. полином)
procedure SetGF;
// П/п вычисления двойственного базиса поля
procedure SetGFDBazis;
// П/п освобождения памяти
procedure GFFreeMem;

var
   // Параметры поля Галуа
    gf_stepen: integer;         // Степень поля
    gf_stepenm1: integer;       // Степень поля за вычетом 1 (для всяких циклов)
    gf_stepenp1: integer;       // Степень поля плюс 1 (для всяких циклов и выделения памяти)
    gf_count:  integer;         // Число элементов поля
    gf_countm1: integer;        // Число элементов поля хза вычетом 1 (для всяких циклов)
    gf_vychet: integer;         // Вычет образующего полинома
    gf_polynom: shortstring;    // Образующий полином
    gf_dbazis: array of TGFElement;      // Двойственный базис поля
    // Массивы для хранения таблицы поля
    dte: array of integer;      // Массив степеней элементов поля по их значению
    etd: array of integer;      // Массив значений элементов поля по их степени

// |-------------------------------------------------------------------|
// |  РАЗДЕЛ ОПРЕДЕЛЕНИЯ ПРОЦЕДУР И ФУНКЦИЙ                            |
// |-------------------------------------------------------------------|
implementation

// == П/П вычисления поля Галуа ========================================
procedure SetGF;
var i,j: integer;   // Счетчик
    l: integer;     // Длина образующего полинома
    s: string;      // Строка для накопления вычета полинома
label z;
begin
    // Делаем проверку: были ли получены степень поля и образующий полином
    if (gf_stepen=0)or(gf_polynom='0') then begin
        writeln('!!! Ошибка: не введен какой-то из параметров поля Галуа');
        halt(0);
    end;
    // == Считаем количество элементов поля
    gf_count := (1 shl gf_stepen) - 1;
    // == Считаем вычет образующего полинома
    l := Length(gf_polynom);
    s := '';
    for i:=0 to l-2 do s := s + gf_polynom[l-i];
    gf_vychet := BinToDec(s);
    // == Считаем пределы для циклов
    gf_stepenm1 := gf_stepen - 1;
    gf_stepenp1 := gf_stepen + 1;
    gf_countm1 := gf_count - 1;
    // == Считаем таблицы значений и степеней элементов поля
    // Выделяем память под таблицы
    SetLength(dte, gf_count+1);
    SetLength(etd, gf_count);
    // Прописываем начальные значения
    dte[0] := 0;
    i := 1 shl gf_stepenm1;
    etd[0] := i;
    dte[i] := 0;
    // Начинаем перебор значений и запись их в таблицы
    j := 1;
    repeat
        asm
            SHR i,1
            JNC z
            MOV EAX,i
            XOR EAX,gf_vychet
            MOV i,EAX
         z: NOP
        end;
        etd[j] := i;
        dte[i] := j;
        Inc(j);
    until(j=gf_count);
    // == Выводим параметры поля
    writeln;
    writeln('Задано поле:   GF(2^', IntToStr(gf_stepen),')');
    writeln('Обр-й полином: p(x) = ', VekToPol(gf_polynom), ' = ', ConstBin(gf_polynom, gf_stepen+1));
    writeln('Вычет полинома: ',IntToStr(gf_vychet));
    writeln;
end;
// ---------------------------------------------------------------------

// == П/п вычисления дв. базиса поля ===================================
procedure SetGFDBazis;
var i: integer;      // Счетчики
   e0,e1: TGFElement;   // Переменные для хранения e^0=1 и e^1=e элементов поля
   proizv: TGFElement;   // Переменная для хранения производной
begin
   // Инициализируем массив дв. базиса
   SetLength(gf_dbazis, gf_stepenp1);            // Выделяем память
   for i:=0 to gf_stepen do gf_dbazis[i]:=TGFElement.Create;   // Создаем элементы массива -- Нулевой элемент в дальн. использоваться не будет
   { Считаем дв. базис по формуле
   * a_i = SUM_[j=0]^[k-i] [p_[k-i-j]*e^j] / p'(e)
   * где i = 1,2,...,k; GF(2^k)
   * e = элемент поля степени 1}
   // Создаем переменные для хранения e^0=1 и e^1=e элементов поля и инициализируем их
   e0 := TGFElement.Create;
   e1 := TGFElement.Create;
   e0 := 0;
   e1 := 1;
   // Cоздаем переменную для хранения производной образ. полинома
   proizv := TGFElement.Create;
   // Считаем производную p'(e)
   if gf_polynom[gf_stepen]='1' then proizv:=0;
   for i:=gf_stepen-1 downto 1 do
      if (((gf_stepenp1-i) mod 2)<>0)and(gf_polynom[i]='1') then
         proizv := proizv + (e1**(gf_stepenp1-i-1));
   // Вычисляем последний элемент дв. базиса
   gf_dbazis[gf_stepen] := e0 / proizv;
   // Вычисляем остальные элементы дв. базиса как
   // db[i-1] := (p_[gf_stepen-i] / p'(e)) + (e * db[i])
   // см. пример 4.10 из книги Когновицкого О.С. "Двойственный базис ..."
   for i:=gf_stepenm1 downto 1 do begin
      gf_dbazis[i] := e1 * gf_dbazis[i+1];
      if gf_polynom[gf_stepen-i+1]='1' then
         gf_dbazis[i] := gf_dbazis[i] + (e0 / proizv);
   end;
   // Выводим дв. базис
   write('Дв. базис: ');
   for i:=1 to gf_stepen do
      write(gf_dbazis[i].print,' ');
   writeln;
   // Уничтожаем служебные переменные
   FreeAndNil(e0);
   FreeAndNil(e1);
   FreeAndNil(proizv);
end;
// ---------------------------------------------------------------------

// == П/п освобождения памяти ==========================================
procedure GFFreeMem;
var i: integer;      // Счетчики
begin
   // Уничтожаем объекты
   for i:=0 to gf_stepen do FreeAndNil(gf_dbazis[i]);
   // Удаляем динамический массив
   gf_dbazis := nil;
end;
// ---------------------------------------------------------------------

// == Конструктор элемента поля == начало
constructor TGFElement.Create;
begin
   z := 0;
end;
// -- Конструктор элемента поля -- конец

// == Обнуление элемента == начало
procedure TGFElement.zero;
begin
   z := 0;
end;
// -- Обнуление элемента -- конец

// == Возвращает TRUE, если элемент ненулевой == начало
function TGFElement.ifnotzero: boolean;
begin
   if z=0 then ifnotzero := false
         else ifnotzero := true;
end;
// --  Возвращает TRUE, если элемент ненулевой -- конец

// == Возвращает показатель степени элемента поля == начало
function TGFElement.exponent: integer;
begin
   if z>0 then   exponent := dte[z]
          else exponent := -1;
end;
// -- Возвращает показатель степени элемента поля -- конец

// == Вывод элемента поля в обычную строку == начало
function TGFElement.print: string;
begin
   if z > 0 then begin
      if dte[z]=0 then print:='1'
        else if dte[z]=1 then print:='e'
        else print:='e'+IntToStr(dte[z])
    end
    else print:='0';
end;
// -- Вывод элемента поля в обычную строку -- конец

// == Вывод элемента поля в строку TeX == начало
function TGFElement.printtex: string;
begin
   if z > 0 then begin
      if dte[z]=0 then printtex:='1'
        else if dte[z]=1 then printtex:='\varepsilon'
        else printtex:='\varepsilon^{'+IntToStr(dte[z])+'}'
    end
    else printtex:='0';
end;
// -- Вывод элемента поля в строку TeX -- конец

// == Деструктор элемента поля == начало
destructor TGFElement.Destroy;
begin
   //
end;
// -- Деструктор элемента поля -- конец

// == Оператор '+' для элемента поля == начало
operator +(const a,b: TGFElement)r: TGFElement;
begin
   r := TGFElement.Create;
   r.z := a.z xor b.z;
end;
// -- Оператор '+' для элемента поля -- конец

// == Оператор '*' для элемента поля == начало
operator *(const a,b: TGFElement)r: TGFElement;
begin
   r := TGFElement.Create;
   if (a.z=0)or(b.z=0) then r.z:=0
    else begin
        r.z := dte[a.z] + dte[b.z];
        // Проверяем на выход из диапазона
        while r.z>=gf_count do r.z := r.z - gf_count;
        while r.z<0 do r.z := r.z + gf_count;
        // Возвращаем значение
        r.z := etd[r.z];
    end;
end;
// -- Оператор '*' для элемента поля -- конец

// == Оператор '/' для элемента поля == начало
operator /(const a,b: TGFElement)r: TGFElement;
begin
   r := TGFElement.Create;
   if (a.z=0) then r.z:=0
    else if b.z=0 then begin
        writeln('!!! Ошибка: Деление на ноль');
        halt(0);
    end
    else begin
        r.z := dte[a.z] - dte[b.z];
        // Проверяем на выход из диапазона
        while r.z<0 do r.z := r.z + gf_count;
        r.z := etd[r.z];
    end;
end;
// -- Оператор '/' для элемента поля -- конец

// == Оператор '**' (возв. в степень) для элемента поля == начало
operator **(const a: TGFElement; const b: integer)r: TGFElement;
begin
   r := TGFElement.Create;
   if (a.z=0) then r.z:=0
    else begin
        r.z := dte[a.z] * b;
        // Проверяем на выход из диапазона
        while r.z>=gf_count do r.z := r.z - gf_count;
        while r.z<0 do r.z := r.z + gf_count;
        // Возвращаем значение
        r.z := etd[r.z];
    end;
end;
// -- Оператор '**' (возв. в степень) для элемента поля -- конец

// == Оператор 'TGFEl := integer' для элемента поля == начало
operator :=(a: integer)r: TGFElement;
begin
   r := TGFElement.Create;
   //while a<0 do a := a + gf_count;
   //while a>=gf_count do a := a - gf_count;
   if a>=0 then r.z := etd[a];
   if a=-1 then r.z := 0;
end;
// -- Оператор 'TGFEl := integer' для элемента поля -- конец

end.


3. convert.pas
Код: Выделить всё
unit convert;

{$mode objfpc}{$H+}
{$ASMMODE INTEL}

interface

uses classes, sysutils;
 
// Функции преобразования
function DecToBin(d: integer): string;
function BinToDec(s: string): integer;
function PolToVek(s: string): string;
function VekToPol(s: string): string;
function ConstBin(s: string; n: integer): string;           //
function ConstStr(s: string; n: integer; z: char): string;  //

const
    // Символы, использующиеся при записи DEC числа
    SetDec: set of char = ['-','0','1','2'..'9'];
    // Символы, использующиеся при записи BIN числа
    SetBin: set of char = ['0','1'];
    // Символы, использующиеся для записи операций
    SetDiv: set of char = ['+','*','/'];
    // Символы десятичных цифр
    SetNum: set of char = ['0','1','2'..'9'];
    // Символы, использующиеся для записи операций
    SetOpr: set of char = ['+','*','/','^'];

implementation

{== Преобразует DEC в BIN =====================================================}
function DecToBin(d: integer): string;
var w,j: integer;
    str: string;
begin
  w := d;
  str := '';

  if w=0 then str := '0'
  else begin
    j := 1073741824;
    //writeln('w=',IntToStr(w),'  j=',IntToStr(j1));
    while (w<j) do j := j div 2;
    //writeln('w=',IntToStr(w),'  j=',IntToStr(j1));
    repeat
        str:=str+IntToStr(w div j);
        if w>=j then w := w - j;
        j := j div 2;
        //writeln('w=',IntToStr(w),'  j=',IntToStr(j1), ' str=',str1);
    until(j=0);
  end;
  DecToBin := str;
end;
{------------------------------------------------------------------------------}

{== Преобразует BIN в DEC =====================================================}
function BinToDec(s: string): integer;
var w, l, r: integer;
begin
  l := Length(s);
  r := 0;
  for w:=l downto 1 do begin
    if s[w]='1' then
      r := r + (1 shl (l - w));
  end;

  BinToDec := r;
end;
{------------------------------------------------------------------------------}

{== Преобразует POL в VEK =====================================================}
function PolToVek(s: string): string;
var j, l: integer;     // Счетчик
    tek, max: integer;
    s_num: string;     // Сюда читается степень при 'x'
    s_vk: string;      // Строка в которую вначале читается вектор
begin
  s_vk := '';

  l := length(s);
  j := 1;
  max := 0;                                   // Ищем максимальную степень
  repeat
    if s[j]='x' then begin
      j := j + 1;
      s_num := '';
      repeat
        s_num := s_num + s[j];
        j := j + 1;
      until((j > L) or not(s[j] in SetDec));
      tek := StrToInt(s_num);
      if tek>max then max:=tek
    end
    else
      j := j + 1;
  until(j > l);

  for j:=max downto 2 do begin                // Прогоняем все степени от максимальной до 2
    l := 0;
    while Pos('x'+IntToStr(j), s)>0 do begin
      tek := Pos('x'+IntToStr(j), s);
      if (tek>0) then l := l xor 1;
      Delete(s, tek, Length(IntToStr(j)) + 1);
    end;
    s_vk := s_vk + IntToStr(l);
  end;
  l := 0;                                     // Прогоняем степень 1 : х1
  while Pos('x1', s)>0 do begin
    tek := Pos('x1', s);
    if (tek>0) then l := l xor 1;
    Delete(s, tek, Length(IntToStr(j)) + 1);
  end;
  s_vk := s_vk + IntToStr(l);
  l := 0;                                    // Прогоняем степень 0 : 1
  while Pos('1', s)>0 do begin
    tek := Pos('1', s);
    if (tek>0) then l := l xor 1;
    Delete(s, tek, Length(IntToStr(j)) + 1);
  end;
  s_vk := s_vk + IntToStr(l);
 
  PolToVek := s_vk;
end;
{------------------------------------------------------------------------------}

{== Преобразует VEK в POL =====================================================}
function VekToPol(s: string): string;
var u: integer;      // Счетчик
    len: integer;    // Длина вектора
    s_pl: string;    // Cюда читается полином
begin
  len := Length(s);
  s_pl := '';

  if BinToDec(s)=0 then s_pl := '0'
  else begin
    for u:=1 to len-1 do begin
      if s[u]='1' then
        s_pl := s_pl + 'x' + IntToStr(len-u) +'+';
    end;

    if s[len]='1' then
      s_pl := s_pl + '1'
    else
      Delete(s_pl, Length(s_pl), 1);
  end;
 
  VekToPol := s_pl;
end;
{------------------------------------------------------------------------------}

{== Приводит BIN число к определенной длине ===================================}
function ConstBin(s: string; n: integer): string;
var l, j: integer;
    add: string;
begin
  l := Length(s);
  add := '';
  if n>l then begin
    for j:=1 to n-l do
      add := add + '0';
  end;
  ConstBin := add + s;
end;
{------------------------------------------------------------------------------}

{== Дополняет строку символами ло определенной длины ==========================}
function ConstStr(s: string; n: integer; z: char): string;
var l, j: integer;
    add: string;
begin
  l := Length(s);
  add := '';
  if n>l then begin
    for j:=1 to n-l do
      add := add + z;
  end;
  ConstStr := add + s;
end;
{------------------------------------------------------------------------------}

end.


4. Вывод valgrind
Код: Выделить всё
$ valgrind --trace-children=yes --leak-check=full ./fpc-model
==30885== Memcheck, a memory error detector
==30885== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==30885== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==30885== Command: ./fpc-model
==30885==

Задано поле:   GF(2^5)
Обр-й полином: p(x) = x5+x3+1 = 101001
Вычет полинома: 18

Дв. базис: 1 e30 e29 e2 e

==30885==
==30885== HEAP SUMMARY:
==30885==     in use at exit: 260 bytes in 13 blocks
==30885==   total heap usage: 68 allocs, 55 frees, 11,490 bytes allocated
==30885==
==30885== 20 bytes in 1 blocks are definitely lost in loss record 1 of 8
==30885==    at 0x4C2815C: malloc (vg_replace_malloc.c:236)
==30885==    by 0x42E1C6: CMEM_CGETMEM$QWORD$$POINTER (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x4326A5: GFCLASSES_SETGFDBAZIS (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x40FC68: main (fpc-model.pas:31)
==30885==
==30885== 20 bytes in 1 blocks are definitely lost in loss record 2 of 8
==30885==    at 0x4C2815C: malloc (vg_replace_malloc.c:236)
==30885==    by 0x42E1C6: CMEM_CGETMEM$QWORD$$POINTER (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x4326C2: GFCLASSES_SETGFDBAZIS (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x40FC68: main (fpc-model.pas:31)
==30885==
==30885== 20 bytes in 1 blocks are definitely lost in loss record 3 of 8
==30885==    at 0x4C2815C: malloc (vg_replace_malloc.c:236)
==30885==    by 0x42E1C6: CMEM_CGETMEM$QWORD$$POINTER (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x4326FB: GFCLASSES_SETGFDBAZIS (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x40FC68: main (fpc-model.pas:31)
==30885==
==30885== 20 bytes in 1 blocks are definitely lost in loss record 4 of 8
==30885==    at 0x4C2815C: malloc (vg_replace_malloc.c:236)
==30885==    by 0x42E1C6: CMEM_CGETMEM$QWORD$$POINTER (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x432F40: GFCLASSES_plus$TGFELEMENT$TGFELEMENT$$TGFELEMENT (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x43279E: GFCLASSES_SETGFDBAZIS (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x40FC68: main (fpc-model.pas:31)
==30885==
==30885== 20 bytes in 1 blocks are definitely lost in loss record 5 of 8
==30885==    at 0x4C2815C: malloc (vg_replace_malloc.c:236)
==30885==    by 0x42E1C6: CMEM_CGETMEM$QWORD$$POINTER (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x432F90: GFCLASSES_star$TGFELEMENT$TGFELEMENT$$TGFELEMENT (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x43280A: GFCLASSES_SETGFDBAZIS (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x40FC68: main (fpc-model.pas:31)
==30885==
==30885== 20 bytes in 1 blocks are definitely lost in loss record 6 of 8
==30885==    at 0x4C2815C: malloc (vg_replace_malloc.c:236)
==30885==    by 0x42E1C6: CMEM_CGETMEM$QWORD$$POINTER (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x4330A4: GFCLASSES_slash$TGFELEMENT$TGFELEMENT$$TGFELEMENT (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x43284C: GFCLASSES_SETGFDBAZIS (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x40FC68: main (fpc-model.pas:31)
==30885==
==30885== 40 bytes in 2 blocks are definitely lost in loss record 7 of 8
==30885==    at 0x4C2815C: malloc (vg_replace_malloc.c:236)
==30885==    by 0x42E1C6: CMEM_CGETMEM$QWORD$$POINTER (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x4331C7: GFCLASSES_starstar$TGFELEMENT$LONGINT$$TGFELEMENT (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x432792: GFCLASSES_SETGFDBAZIS (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x40FC68: main (fpc-model.pas:31)
==30885==
==30885== 100 bytes in 5 blocks are definitely lost in loss record 8 of 8
==30885==    at 0x4C2815C: malloc (vg_replace_malloc.c:236)
==30885==    by 0x42E1C6: CMEM_CGETMEM$QWORD$$POINTER (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x432673: GFCLASSES_SETGFDBAZIS (in /home/vlss/Project/free-pascal/fpc-modelirovanie/fpc-model)
==30885==    by 0x40FC68: main (fpc-model.pas:31)
==30885==
==30885== LEAK SUMMARY:
==30885==    definitely lost: 260 bytes in 13 blocks
==30885==    indirectly lost: 0 bytes in 0 blocks
==30885==      possibly lost: 0 bytes in 0 blocks
==30885==    still reachable: 0 bytes in 0 blocks
==30885==         suppressed: 0 bytes in 0 blocks
==30885==
==30885== For counts of detected and suppressed errors, rerun with: -v
==30885== ERROR SUMMARY: 8 errors from 8 contexts (suppressed: 4 from 4)
Rakshas
новенький
 
Сообщения: 12
Зарегистрирован: 12.07.2009 23:53:18

Re: Утечка памяти в программе

Сообщение *vmr » 13.02.2011 13:02:10

Утечка в операторах

Оператор на вход получает два класса. Создает результат.
Этот результат постоянно пишется в массив, перезатирая ссылку на уже созданный класс.

Пример:
Было e0, pozitv и старое значение gf_dbazis[gf_stepen]
После
Код: Выделить всё
   // Вычисляем последний элемент дв. базиса
   gf_dbazis[gf_stepen] := e0 / proizv;

стало e0, pozitv и новое значение gf_dbazis[gf_stepen], и старое значение gf_dbazis[gf_stepen] ссылка на которое безвозвратно потеряна (утечка)
Аватара пользователя
*vmr
постоялец
 
Сообщения: 168
Зарегистрирован: 08.01.2007 01:46:07
Откуда: Киев

Re: Утечка памяти в программе

Сообщение Rakshas » 13.02.2011 15:16:14

*vmr писал(а):Утечка в операторах

Ясно. А как это можно исправить?
Rakshas
новенький
 
Сообщения: 12
Зарегистрирован: 12.07.2009 23:53:18

Re: Утечка памяти в программе

Сообщение hinst » 13.02.2011 15:36:17

уничтожайте свой gf_dbazis[gf_stepen] перед присваиванием в него
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Утечка памяти в программе

Сообщение Rakshas » 13.02.2011 16:43:21

hinst писал(а):уничтожайте свой gf_dbazis[gf_stepen] перед присваиванием в него

Ух. Спасибо огромное. Все работает без утечек.

Тогда доп. вопрос: А если использовать вместо класса объект, можно ли будет не уничтожать экземпляр? (Как я понимаю, доп. операции уничтожения будут тормозить программу (т.к. там будут очень большие циклы)) Или с объектом нельзя перегрузить операторы?
Rakshas
новенький
 
Сообщения: 12
Зарегистрирован: 12.07.2009 23:53:18

Re: Утечка памяти в программе

Сообщение Vadim » 13.02.2011 16:54:35

Rakshas писал(а):А если использовать вместо класса объект, можно ли будет не уничтожать экземпляр?

А какая разница? Если Вы не уничтожаете то количество выделенной памяти от которого отказваетесь, в любом виде это количество памяти будет присутствовать в качестве мусора, вне зависимости от того, ктаким образом этот мусор произведён. ;) Прорблема то у Вас с памятью, а не с быстродействием.
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Утечка памяти в программе

Сообщение Sergei I. Gorelkin » 13.02.2011 17:26:58

У данного примера есть принципиальная проблема. Любое выражение с более чем одним действием, например

a := b + c + d + e;

будет выполняться как

tmp1 := b + c;
tmp2 := tmp1 + d;
a := tmp2 + e;

при этом tmp1 и tmp2 (экземпляры, создаваемые оператором "+") утекут. Собственно, их вообще невозможно освободить.

Поэтому вместо классов нужно использовать объекты или записи, которые они могут размещаться целиком в стеке. Тогда в операторах не нужно будет выделять память, и соответственно не нужно будет ее освобождать. Операторы для объектов/записей перегружать можно.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1406
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Утечка памяти в программе

Сообщение hinst » 13.02.2011 21:35:11

Если объявить как Object, то утекать не буит, ибо будет старый обжект затираеться сам собой при присваивании.
:idea: а насчёт того, что сказал Горелкин: предлагаю суперсредство. Два конструктора. Один помечает объект как юзверьский, другой создаёт объект как временный... и когда result = nil, то создавать в операторе результат временным конструктором, иначе класть в уже существующий result результат. иначе делать временным и потом в том же операторе если один из операндов временный, то тереть его... ну и ясно, что я хочу сказать, да?
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Утечка памяти в программе

Сообщение Иван Шихалев » 13.02.2011 22:08:43

Можно воспользоваться типами со счетчиком ссылок. Например, COM-интерфейсами:
Код: Выделить всё
{$mode objfpc}{$h+}

program Oper;

type
  IComplex = interface(IUnknown)
  // property access
    function getRe : double;
    function getIm : double;
    procedure setRe (const Val : double);
    procedure setIm (const Val : double);
  // properties
    property Re : double read getRe write setRe;
    property Im : double read getIm write setIm;
  end;

type
  TComplex = class(TInterfacedObject, IComplex)
  private
    fldRe, fldIm : double;
    function getRe : double;
    function getIm : double;
    procedure setRe (const Val : double);
    procedure setIm (const Val : double);
  public
    property Re : double read getRe write setRe;
    property Im : double read getIm write setIm;
  public
    constructor Create(const vRe, vIm : double);
    destructor Destroy; override;
  end;

function TComplex.getRe : double;
begin
  result := fldRe;
end;

function TComplex.getIm : double;
begin
  result := fldIm;
end;

procedure TComplex.setRe (const Val : double);
begin
  fldRe := Val;
end;

procedure TComplex.setIm (const Val : double);
begin
  fldIm := Val;
end;

constructor TComplex.Create (const vRe, vIm : double);
begin
  inherited Create;
  fldRe := vRe;
  fldIm := vIm;
  WriteLn('Create: ', fldRe, ', ', fldIm);
end;

destructor TComplex.Destroy;
begin
  WriteLn('Destroy: ', fldRe, ', ', fldIm);
  inherited Destroy;
end;

operator + (x, y : IComplex) : IComplex;
begin
  result := TComplex.Create(x.Re + y.Re, x.Im + y.Im);
end;

function Complex (const vRe, vIm : double) : IComplex;
begin
  result := TComplex.Create(vRe, vIm);
end;

procedure do_something;
var
   x, y, z : IComplex;
begin
  x := Complex(1,1) + Complex(2, 3);
  y := x + Complex(11, 12);
  z := Complex(Pi,0) + Complex(0, 0);
  WriteLn('end proc;');
end;

begin
do_something;
WriteLn('end.');
end.


Правда, результат получается довольно интересный:
Код: Выделить всё
iws@ivan ~/wrk/projects/tmp $ ./oper
Create:  2.00000000000000E+000,  3.00000000000000E+000
Create:  1.00000000000000E+000,  1.00000000000000E+000
Create:  3.00000000000000E+000,  4.00000000000000E+000
Destroy:  1.00000000000000E+000,  1.00000000000000E+000
Create:  1.10000000000000E+001,  1.20000000000000E+001
Create:  1.40000000000000E+001,  1.60000000000000E+001
Destroy:  1.10000000000000E+001,  1.20000000000000E+001
Create:  0.00000000000000E+000,  0.00000000000000E+000
Destroy:  2.00000000000000E+000,  3.00000000000000E+000
Create:  3.14159265358979E+000,  0.00000000000000E+000
Create:  3.14159265358979E+000,  0.00000000000000E+000
end proc;
Destroy:  0.00000000000000E+000,  0.00000000000000E+000
Destroy:  3.14159265358979E+000,  0.00000000000000E+000
Destroy:  3.00000000000000E+000,  4.00000000000000E+000
Destroy:  1.40000000000000E+001,  1.60000000000000E+001
Destroy:  3.14159265358979E+000,  0.00000000000000E+000
end.

промежуточные вычисления освобождаются с некоторым запаздыванием. Но при выходе из процедуры все равно освобождаются полностью, так что запаздывание не критично.
Аватара пользователя
Иван Шихалев
энтузиаст
 
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург

Re: Утечка памяти в программе

Сообщение Rakshas » 14.02.2011 01:45:15

hinst писал(а):... ну и ясно, что я хочу сказать, да?

Признаюсь честно: не очень. Хорошо бы с примером.
Просто это мой первый опыт работы с классами. До этого обходился массивами, а тут узнал про возможность перегрузки операторов и решил сделать. Вот и столкнулся с проблемой.

Иван Шихалев писал(а):Можно воспользоваться типами со счетчиком ссылок. Например, COM-интерфейсами:
...
промежуточные вычисления освобождаются с некоторым запаздыванием. Но при выходе из процедуры все равно освобождаются полностью, так что запаздывание не критично.

Там весь фокус в том, что в процедурах самой модели, в циклах идет перебор миллионов комбинаций, на каждую из которых идет не один десяток вычислений. Так что память (вместе со swap'ом) съестся задолго до конца процедуры.

Пока буду переписывать в виде объектов.
Rakshas
новенький
 
Сообщения: 12
Зарегистрирован: 12.07.2009 23:53:18

Re: Утечка памяти в программе

Сообщение Иван Шихалев » 14.02.2011 01:59:25

Так ведь часть освобождается до выхода из процедуры. Так что я бы попробовал.

Добавлено спустя 1 минуту 46 секунд:
С другой стороны, если данные статичны, то, конечно, пойдет и object...
Аватара пользователя
Иван Шихалев
энтузиаст
 
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург

Re: Утечка памяти в программе

Сообщение hinst » 14.02.2011 22:26:10

перегрузка операторов работает, если что, не только с классами, а с любыми типами данных.
а с той фигнёй:
Код: Выделить всё
operator + (const a, b: TMy): TMy;
begin
  if result = nil then result:=TMy.CreateTemp();
  ...тут складываем...
  if a.temp then a.free;
  if b.temp then b.free;
end;

основной код:
a:=TMy.Create();
a:=b+c+d+e;
и не забыть в конструкторе CreateTemp назначать ему свойство temp в true
и имхо никаких утечек быть не должнова...
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Утечка памяти в программе

Сообщение Иван Шихалев » 15.02.2011 17:04:53

hinst писал(а):a:=TMy.Create();
a:=b+c+d+e;

Это уже утечка — первое значение a (TMy.Create) висит в воздухе.
Нужен подсчет ссылок на уровне компилятора. А он есть только для строк, динамических массивов и интерфейсов.

Добавлено спустя 1 минуту 50 секунд:
hinst писал(а):if result = nil then

Бессмысленно. Оператор ничего не знает о том, куда его присваивают.
Аватара пользователя
Иван Шихалев
энтузиаст
 
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург

Re: Утечка памяти в программе

Сообщение hinst » 15.02.2011 20:02:15

Иван Шихалев
ничего подобного, оператор знает, куда его присваивают, в том и фигня
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Утечка памяти в программе

Сообщение Иван Шихалев » 15.02.2011 21:55:31

hinst писал(а): оператор знает, куда его присваивают

Неправда, мусор там — результат предыдущих действий:
Код: Выделить всё
{$mode objfpc}{$h+}

program OperA;

type
  TNum = record
    num : PtrInt;
  end;

operator := (x : PtrInt) : TNum;
begin
  WriteLn('convert: ', result.num, ' <= ', x);
  result.num := x;
end;

operator + (x, y : TNum) : TNum;
begin
  WriteLn('summa: ', result.num, ' <= ', x.num, ' + ', y.num);
  result.num := x.num + y.num;
end;

var
  a, b : TNum;

begin
a := 10;
b := TNum(20) + 1;
a := 5;
end.

Выдает:
Код: Выделить всё
iws@ivan ~/wrk/tmp $ ./opera
convert: 0 <= 10
convert: 10 <= 1
convert: 1 <= 20
summa: 20 <= 20 + 1
convert: 21 <= 5

Т.е. в последнем случае в result лежит значение b, тогда как присваивание происходит a.
Аватара пользователя
Иван Шихалев
энтузиаст
 
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург

След.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 6

Рейтинг@Mail.ru