![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Утечка AnsiString [D7, XP]
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); и т.д.