Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2008.11.23;
Скачать: CL | DM;

Вниз

Утечка AnsiString   Найти похожие ветки 

 
kami ©   (2008-10-11 01:18) [0]

Проверяю с помощью 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]


 
Германн ©   (2008-10-11 01:44) [1]


> Самое интересное - при создании максимально упрощенного
> примера утечек не происходит...

Ну а что ты хочешь получить в ответ?
" у тебя ошибка в 17-й строке, которая в сабже представлена как "..."?
Не, ну можно ещё припомнить "подземный стук", но это не меняет сути.
Реальный ответ и реальную помощь в сложных ситуациях можно получить только дав реальный код. Код, который даёт ошибку. И который можно воспроизвести на любом компе, на котором установлена  та версия Дельфи.
,


 
Юрий Зотов ©   (2008-10-11 08:47) [2]

destructor TDataObject.Destroy;
begin
 FHeader.SomeFieldx := "";
 FHeader.SomeFieldy := "";
 inherited
end;


 
Palladin ©   (2008-10-11 10:35) [3]


> Юрий Зотов ©   (11.10.08 08:47) [2]

в деструкторе вызываeтся FinalizeRecord, думаю причина в другом... реальный код нужен...

аффтору рекомендую постепенно упрощать модель пока не будет все в порядке, потом глянуть что же упростил...


 
Юрий Зотов ©   (2008-10-11 10:44) [4]

> Palladin ©   (11.10.08 10:35) [3]

Насколько я понимаю, если память под record была выделена через GetMem, а освобождена через FreeMem, то финализировать надо ручками.

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


 
Palladin ©   (2008-10-11 10:59) [5]


> если память под record была выделена через GetMem, а освобождена
> через FreeMem

если это так, то, понятное дело - автор ССЗБ...


 
kami ©   (2008-10-11 12:18) [6]

> Германн ©   (11.10.08 01:44) [1]
> Ну а что ты хочешь получить в ответ?
> " у тебя ошибка в 17-й строке
> Реальный ответ и реальную помощь в сложных ситуациях можно
> получить только дав реальный код.


Это я прекрасно понимаю. Думал, может кто сталкивался с подобным. Минимальный код, дающий ошибку...попробую

> Юрий Зотов ©   (11.10.08 08:47) [2]
FHeader.SomeFieldx := "";

Пробовал изначально. Не помогает.

>Юрий Зотов ©   (11.10.08 10:44) [4]
> если память под record была выделена через GetMem, а освобождена
> через FreeMem, то финализировать надо ручками.

Гхм... разве что это сделал сам компилятор. В этом случает GetMem|AllocMem даже близко не пахнет.
Хорошо, я понял, что нужен работающий (вернее, утекающий) код. Попробую его сообразить.


> Palladin ©   (11.10.08 10:59) [5]
>если это так, то, понятное дело - автор ССЗБ
.
Спасибо на добром слове :) Отрицать не буду, вполне возможно :)


 
Palladin ©   (2008-10-11 12:21) [7]


> Гхм... разве что это сделал сам компилятор.

Компилятор не будет этого делать, при штатной работе с записями (не указателями на запись, а с ними самими), то все будет нормально, в деструкторе запись финализируется. Потому и нужен реальный код в котором все происходит. То что приведено в самом вопросе, никаких нареканий вызывать не может.


 
Anatoly Podgoretsky ©   (2008-10-11 12:22) [8]

Стучит, стучит в подвале.


 
oxffff ©   (2008-10-11 12:47) [9]


> kami ©   (11.10.08 01:18)  


А как ты сделал вывод, что проблема именно в TSomeRecord?
Может не освобождается что-то другое?
Например технически очень просто организовать финализацию объекта, но освобождение памяти под объекта сделать позже.


 
kami ©   (2008-10-11 19:31) [10]

oxffff ©   (11.10.08 12:47) [9]
> А как ты сделал вывод, что проблема именно в TSomeRecord?

Лог FastMM:
462A55 [F:\...\uDataObject.pas][uDataObject][TDataObject.Create][110]
На строке 110 этого модуля:
FHeader := Header;
Все, что вызвано ранее - транзит между модулями до достижения непосредственно конструктора объекта.
Palladin ©   (11.10.08 12:21) [7]
> Компилятор не будет этого делать, при штатной работе с записями
> (не указателями на запись, а с ними самими)

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


 
oxffff ©   (2008-10-11 19:50) [11]


> kami ©   (11.10.08 19:31) [10]


А как ты делаешь вывод, что именно в этой строке утечка памяти?
Твои умозаключения на чем построены?

FHeader := Header соответствует
404EDD [System][@CopyRecord]
судя по названию


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

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


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

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;


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

Всем спасибо.
В результате попыток упростить код до неузнаваемости (т.е. до того момента, когда еще можно воспроизвести утечку) было выявлено следующее:
Уже после создания пресловутого объекта состояние обработки данных в нем "мониторилось" другим потоком.
Данные в него передавались следующим образом:
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:36) [15]

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


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

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

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

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


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


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

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

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


 
Riply ©   (2008-10-12 02:04) [18]

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

Спасибо :)


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


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

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


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


> 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, поэтому и надо "диспозить" с тем же типом, с каким и создавали, дабы компилятор понял, какой кусок памяти освобождать.


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

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.


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


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

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

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

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

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


 
kami ©   (2008-10-12 14:29) [23]

> 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, поэтому поиски ошибки и заняли так долго...


 
kami ©   (2008-10-12 14:31) [24]

> Sapersky   (12.10.08 13:19) [21]

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


 
Palladin ©   (2008-10-12 14:34) [25]


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

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


 
Anatoly Podgoretsky ©   (2008-10-12 15:10) [26]

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

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


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


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

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


 
oxffff ©   (2008-10-12 19:24) [28]


> Sapersky   (12.10.08 13:19) [21]


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


 
kami ©   (2008-10-12 20:10) [29]

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

в D7 ?


 
oxffff ©   (2008-10-12 21:03) [30]


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


в 2009. ;)


 
oxffff ©   (2008-10-12 21:25) [31]


> 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]

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

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

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



Страницы: 1 вся ветка

Текущий архив: 2008.11.23;
Скачать: CL | DM;

Наверх




Память: 0.57 MB
Время: 0.02 c
2-1223903376
VIP77
2008-10-13 17:09
2008.11.23
Как получить login текущего пользователя?


2-1223763095
aslanbek
2008-10-12 02:11
2008.11.23
Обработки ошибок.


2-1223625754
asders
2008-10-10 12:02
2008.11.23
HotKey


2-1223840729
programmer90
2008-10-12 23:45
2008.11.23
Особенности String переменной


3-1210168602
dreamse
2008-05-07 17:56
2008.11.23
Пробелмы с компонентами