Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Утечка AnsiString [D7, XP]


Страницы:   1 2

kami ©   (2008-10-11 01:18:00) 

Проверяю с помощью FastMM.

Есть структура, которая передается в конструктор объекта. Структура содержит 2 поля типа string:
TSomeRecord=packed record
 SomeField1:integer;
//.........
 SomeFieldx:string;
 SomeFieldy:string;
end;
//....................
constructor TDataObject.Create(Header: TSomeRecord; Data: TStream);
begin
 FHeader:=Header;
 //..................
end;

Объект уничтожается штатно (проверено, оттрассировано).
Самое интересное - при создании максимально упрощенного примера утечек не происходит... Сильно подозреваю, что это вызывано тем, что объект создается в одном потоке, а уничтожается в другом (потоки - наследники TThread). Понимаю, что это не есть сильно правильно, но структуру программы поменять не получится :(.
Что можно сделать для ликвидации утечек?

P.S. Чуть не забыл - лог FastMM:
Блок памяти был выделен и не освобожден. Размер: 20

Трассировка стека при выделении блока (вхождения):
40427D [System][@NewAnsiString]
404210 [System][@LStrAsg]
404EDD [System][@CopyRecord]
462A55 [F:\...\uDataObject.pas][uDataObject][TDataObject.Create][110]
463E63 [F:\...\uDataThread.pas][uDataThread][TData.Send][439]
4678CA [Unit1.pas][Unit1][TForm1.Button4Click][119]
4342DA [Controls][TControl.Click]
42CB61 [StdCtrls][TButton.Click]
42CC55 [StdCtrls][TButton.CNCommand]


oxffff   (2008-10-12 21:25:00) [1]


> kami ©   (12.10.08 20:10) [29]


Тебе уже показали как делать правильно

Palladin ©   (12.10.08 13:38) [22]

Dispose( PDataHeader(List[0]))

Либо

jack128_   (12.10.08 18:26) [27]

Finalize(TDataHeader(List[0]^))+Freemem.

Не забивай себе голову полу-универсальными средствами.
Sapersky   (12.10.08 13:19) [21]

Основной минус

-отсутствие строгой типизации.

Никто не мешает добавить в список не то, что должно быть.


oxffff   (2008-10-12 21:03:00) [2]


> kami ©   (12.10.08 20:10) [29]


в 2009. ;)


kami   (2008-10-12 20:10:00) [3]

> oxffff ©   (12.10.08 19:24) [28]

в D7 ?


oxffff   (2008-10-12 19:24:00) [4]


> Sapersky   (12.10.08 13:19) [21]


Уже есть полу-параметризованные классы - generics.


jack128_   (2008-10-12 18:26:00) [5]


>  FHeader.SomeFieldx := '';
>  FHeader.SomeFieldy := '';

для файнализации есть проседура Finalize(MyVar)


Anatoly Podgoretsky   (2008-10-12 15:10:00) [6]

> Palladin  (12.10.2008 14:34:25)  [25]

Вообще то не тип, а правильный, не порушеный указатель.


Palladin   (2008-10-12 14:34:00) [7]


> Да, но создание pDataHeader и заполнение его полей находится
> ни разу не в [TDataObject.Create]

какая разница где оно находится, это вообще к освобождению никакого отношения не имеет. ты должен прямо указатькакой тип ты освобождаешь посредством Dispose


kami   (2008-10-12 14:31:00) [8]

> Sapersky   (12.10.08 13:19) [21]

Спасибо, посмотрю.


kami   (2008-10-12 14:29:00) [9]

> Riply ©   (12.10.08 01:11) [16]
> некоторые борются с утечками путем перезапуска программы

Низя. Если бы утечка была в единственном экземпляре за запуск программы, или она имела ограниченное время жизни - может и не стал бы заморачиваться :)
> А какой тип у FList[0]

Pointer. FList:TList;

> korneley ©   (12.10.08 04:14) [20]
>
> > kami ©   (11.10.08 19:31) [10]
> >Не пользуюсь указателями в этом и близлежащих классах.
> > kami ©   (12.10.08 00:33) [14]
> > var  pHeader: ^TDataHeader;

Угу. Это на самом деле не близлежащий класс, а "хата скраю" :)

> надо "диспозить" с тем же типом, с каким и создавали
Дык, после таких поисков, думаю, из головы это уже не выветрится.

Palladin ©   (12.10.08 13:38) [22]
> логика в том что Dispose для Pointer просто вызывает FreeMem,
> а вот
> PDataHeader=^TDataHeader;
> Dispose(PDataHeader(List[0])) будет финализировать запись

Да, но создание pDataHeader и заполнение его полей находится ни разу не в [TDataObject.Create], на которые указывает FastMM, поэтому поиски ошибки и заняли так долго...


Palladin   (2008-10-12 13:38:00) [10]


> kami ©   (12.10.08 00:36) [15]

логика в том что Dispose для Pointer просто вызывает FreeMem, а вот

PDataHeader=^TDataHeader;
Dispose(PDataHeader(List[0])) будет финализироватьзапись, явно указанную при приведении типов, и потом уже FreeMem.

и FastMM прав на все 100%

и ведь хотел же написать в [5]... но что то поленился :)


Sapersky   (2008-10-12 13:19:00) [11]

Dispose(FList[0]);// вот оно - замена на Dispose(pHeader) - и утечка исчезла.

Ага, вот они - прелести дельфийского TList.

Собрался-таки написать слегка автоматизированный вариант, который позволяет не повторять сотни раз New/Dispose (не делать каждый лист TObjectList'ом) и заодно уменьшить вероятность глупых ошибок вроде [14].
Писал прямо сейчас, толком не тестировал. Кому интересно - погоняйте на предмет тех же утечек. Если не интересно - фиг с ним, времени потрачено немного.

 TRecordList = class(TList)
 private
   FRecSize: Integer;
   FTypeInfo : Pointer;
 protected
   function AllocNew(Src: Pointer): Pointer;
   procedure Notify(Ptr: Pointer; Action: TListNotification); override;
 public
   constructor Create(RecSize: Integer; TypeInfo : Pointer);
   function Add(Src: Pointer): Integer;
   procedure Insert(Index: Integer; Src: Pointer);
 end;

{ TRecordList }

constructor TRecordList.Create(RecSize: Integer; TypeInfo: Pointer);
begin
FRecSize := RecSize; FTypeInfo := TypeInfo;
end;

procedure xInitialize(p: Pointer; typeInfo: Pointer; elemCount: Cardinal);
asm
CALL    System.@InitializeArray;
end;

procedure xFinalize( P: Pointer; Info: Pointer; elemCount: integer );
asm
CALL    System.@FinalizeArray;
end;

procedure xCopy( Dst, Src : Pointer; Info: Pointer; Cnt: Integer);
asm
 push Cnt
 CALL    System.@CopyArray;
//  add esp, 4 // for some reason we don't need stack restoring
end;

function TRecordList.AllocNew(Src: Pointer): Pointer;
begin
If (FRecSize <> 0) then begin
 GetMem(Result, FRecSize);
 If (FTypeInfo <> nil) then xInitialize(Result, FTypeInfo, 1);
 If (Src <> nil) then
   If (FTypeInfo <> nil) then xCopy(Result, Src, FTypeInfo, 1)
                         else System.Move(Src^, Result^, FRecSize);
end else
 Result := Src;
end;

function TRecordList.Add(Src: Pointer): Integer;
begin
Result := Inherited Add( AllocNew(Src) );
end;

procedure TRecordList.Insert(Index: Integer; Src: Pointer);
begin
Inherited Insert(Index, AllocNew(Src) );
end;

procedure TRecordList.Notify(Ptr: Pointer; Action: TListNotification);
begin
If (FRecSize <> 0) and (Action = lnDeleted) then begin
 If (FTypeInfo <> nil) then xFinalize(Ptr, FTypeInfo, 1);
 FreeMem(Ptr);
end;
Inherited Notify(Ptr, Action);
end;

Использование:

Var FList : TRecordList; // обязательно TRecordList - Add/Insert не виртуальные функции

FList := TRecordList.Create(SizeOf(TSomeRecord), TypeInfo(TSomeRecord));
// Если компилятор скажет "type TSomeRecord has no type info" - передавайте в TypeInfo nil

// добавление (New и копирование pHeader^ := Header делается автоматически)
FList.Add(@Header);

// удаление (Dispose делается автоматически)
FList.Delete(0);

Хотя, в общем, хрен редьки не слаще, перепутать RecSize или TypeInfo при вызове конструктора можно так же легко, как и тип в Dispose.


korneley   (2008-10-12 04:14:00) [12]


> kami ©   (11.10.08 19:31) [10]
>Не пользуюсь указателями в этом и близлежащих классах.
> kami ©   (12.10.08 00:33) [14]
> var  pHeader: ^TDataHeader;

:)) Да, ещё, не очень понятно зачем "рекорд" именно "пакед", если стринг без длинны?

> Германн ©   (12.10.08 01:26) [17]
> > Riply ©   (12.10.08 01:11) [16]> > > [15] kami ©   (12.
> 10.08 00:36)> > Правда, я так и не понял логику, по которой
> FastMM указывал > на утечку в конструкторе...> > А какой
> тип у FList[0] ?> Имхо либо Pointer, либо TObject. Скорее второе.

Не соглашусь. Скорее первое :)

> New(pHeader);
> ...
> Dispose(FList[0]);// вот оно - замена на Dispose(pHeader) - и утечка исчезла.

На каждый "нью" - свой, симметричный, "диспоз". А FList[0] просто pointer, поэтому и надо "диспозить" с тем же типом, с каким и создавали, дабы компилятор понял, какой кусок памяти освобождать.


Германн   (2008-10-12 02:30:00) [13]


> Riply ©   (12.10.08 02:04) [18]
>
> > [17] Германн ©   (12.10.08 01:26)
> > докОпался
>
> Спасибо :)
>

Не за что. :)
Приходите ещё. :)


Riply   (2008-10-12 02:04:00) [14]

> [17] Германн ©   (12.10.08 01:26)
> докОпался

Спасибо :)


Германн   (2008-10-12 01:26:00) [15]


> Riply ©   (12.10.08 01:11) [16]
>
> > [15] kami ©   (12.10.08 00:36)
> > Правда, я так и не понял логику, по которой FastMM указывал
> на утечку в конструкторе...
>
> А какой тип у FList[0] ?
>

Имхо либо Pointer, либо TObject. Скорее второе.

А то что автор молодец, что сам док Опался - эт точно. Значит есть обоснованная надежда, что он научится.


Riply   (2008-10-12 01:11:00) [16]

> [15] kami ©   (12.10.08 00:36)
> Правда, я так и не понял логику, по которой FastMM указывал на утечку в конструкторе...

А какой тип у FList[0] ?

P.S.
Молодец, что докапался. Серьезно :)
А то некоторые борются с утечками путем перезапуска программы :)


kami   (2008-10-12 00:36:00) [17]

Правда, я так и не понял логику, по которой FastMM указывал на утечку в конструкторе...( kami ©   (12.10.08 00:22) [13] ).


kami   (2008-10-12 00:33:00) [18]

Всем спасибо.
В результате попыток упростить код до неузнаваемости (т.е. до того момента, когда еще можно воспроизвести утечку) было выявлено следующее:
Уже после создания пресловутого объекта состояние обработки данных в нем "мониторилось" другим потоком.
Данные в него передавались следующим образом:
var
 pHeader: ^TDataHeader; // он же TSomeRecord :)
begin
 New(pHeader);
 pHeader^ := Header;
 FCriticalSection.Enter;
 FList.Add(pHeader);
 FCriticalSection.Leave;
 PostMessage(FWndHandle, msgSend, 0, 0);


Уничтожение шло в оконной процедуре после обработки:
pHeader := FList[0];
// работа с pHeader
FCriticalSection.Enter;
Dispose(FList[0]);// вот оно - замена на Dispose(pHeader) - и утечка исчезла.
FList.Delete(0);
FCriticalSection.Leave;


kami   (2008-10-12 00:22:00) [19]

Sapersky   (11.10.08 21:17) [12]
> Если строки действительно не уничтожаются (а не FastMM глючит)

Его не глючит, MemProof тоже показывает оставшиеся 2 LivePointer (FastMM - 2 AnsiString)

> UniqueString(FHeader.SomeFieldx);

Не помогло, но за напоминание спасибо, пригодится.


> oxffff ©   (11.10.08 19:50) [11]
> А как ты делаешь вывод, что именно в этой строке утечка
> памяти?


constructor TOutDataObject.Create(Header: TDataHeader; Stream: TStream);
begin
 FDataHeader := Header; // комментируем строку и утечки нет. № строки - 110
 FDataStream := Stream;


Sapersky   (2008-10-11 21:17:00) [20]

Если строки действительно не уничтожаются (а не FastMM глючит) - значит, счётчики ссылок по каким-то причинам не обнуляются.
Можно попробовать создать уникальные копии строк после копирования в конструкторе, чтобы избежать махинаций со счётчиками:
FHeader:=Header;
UniqueString(FHeader.SomeFieldx); и т.д.


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо