Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2008.11.23;
Скачать: [xml.tar.bz2];

Вниз

Утечка 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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.55 MB
Время: 0.006 c
15-1222175576
Кое кто
2008-09-23 17:12
2008.11.23
Хочу сыграть один против форума Delphimaster в шахматы...


2-1223560708
sql___
2008-10-09 17:58
2008.11.23
получить все дочерние эл-ты дерева


15-1221915502
xayam
2008-09-20 16:58
2008.11.23
Вот это я понимаю машина


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


11-1195918889
Михаил
2007-11-24 18:41
2008.11.23
KOLZip





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский