Форум: "Начинающим";
Текущий архив: 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