Форум: "Основная";
Текущий архив: 2011.04.10;
Скачать: [xml.tar.bz2];
ВнизDelphi 2009-2010, утекает память в записях Найти похожие ветки
← →
gosha52 (2009-09-03 15:46) [0]Здравствуйте уважаемые,
Хотел узнать у вас, как лечить утечку памяти в случаях, когда в записе есть строки? Покажу простой пример,
TMyRec = record
MyStr1: string;
end;
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
public
MyRec: TMyRec;
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
ReportMemoryLeaksOnShutdown := True;
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
ZeroMemory(@MyRec, SizeOf(TMyRec));
MyRec.MyStr1 := "daskjdaslkjdlasjdвфывфыskl";
end;
Достаточно кликнуть по кнопке два раза, чтобы после закрытия программы FastMM указал на утечку. Если не вызывать ZeroMemory, то всё как бы работает. Даже работает если перед вызовом ZeroMemory вписать строку MyRec.MyStr1 := "", которая по идее вручную очищает память строки. Можно конечно использовать строку с заранее заданной длиной типа ShortString, но мне так неудобно. Да и в старой доброй D7 таких проблем не было. Даже с AnsiString происходит данная утечка в D2009-2010. Помогите разобраться, что не так.
Спасибо.
← →
Омлет (2009-09-03 16:04) [1]> ZeroMemory(@MyRec, SizeOf(TMyRec));
Жуть. Ты зачем это вообще делаешь?
← →
Leonid Troyanovsky © (2009-09-03 16:05) [2]
> gosha52 (03.09.09 15:46)
> программы FastMM указал на утечку. Если не вызывать ZeroMemory,
> то всё как бы работает.
Не очень понятно, зачем потребовалось ZM, бо поля объектов
инициализируются автоматом, причем естественным способом
(RTFM: huge string).
Затем непонятно, зачем потребовались такие чудные record
из одной строки. Ну, т.д.
--
Regards, LVT.
← →
Омлет (2009-09-03 16:05) [3]Когда в записе есть строки, запись имеет переменную длину и никак не равна SizeOf(TMyRec).
← →
gosha52 (2009-09-03 16:13) [4]
> Не очень понятно, зачем потребовалось ZM, бо поля объектов
> инициализируются автоматом, причем естественным способом
> (RTFM: huge string).
Я это понимаю, а что если MyRec глобальная? Мне как-то надо очищать содержимое строк в MyRec.
> Затем непонятно, зачем потребовались такие чудные record
> из одной строки. Ну, т.д.
Это просто пример, чтобы акцентировать внимание на строке.
> Когда в записе есть строки, запись имеет переменную длину
> и никак не равна SizeOf(TMyRec).
Тогда каким образом можно очищать запись без утечки? SizeOf(MyRec) также не помогает.
← →
Sapersky (2009-09-03 16:13) [5]Да и в старой доброй D7 таких проблем не было.
Это не проблема, а нормальное поведение, не зависящее от версии. ZeroMemory никогда не очищал память строк, видимо, раньше вы просто не замечали, что у вас что-то течёт.
В случае с MyRec.MyStr1 := "" перед ZeroMemory никаких утечек быть не должно. Специально проверил - всё нормально, что-то вы путаете.
Кстати, очистить сразу все строки (дин. массивы, варианты, интерфейсы) записи можно через Finalize(MyRec).
Когда в записе есть строки, запись имеет переменную длину и никак не равна SizeOf(TMyRec).
Да нет, почему, равна. Другое дело, что реально памяти выделено больше чем SizeOf(TMyRec).
← →
gosha52 (2009-09-03 16:16) [6]
> Это не проблема, а нормальное поведение, не зависящее от
> версии. ZeroMemory никогда не очищал память строк, видимо,
> раньше вы просто не замечали, что у вас что-то течёт.
Скорее всего да, не замечал.
> Кстати, очистить сразу все строки (дин. массивы, варианты,
> интерфейсы) записи можно через Finalize(MyRec).
О спасибо, странно, но раньше никогда не слышал про эту функцию.
← →
Leonid Troyanovsky © (2009-09-03 16:22) [7]
> gosha52 (03.09.09 16:13) [4]
> Я это понимаю, а что если MyRec глобальная?
Здесь не очень понятно, бо сначала было про объекты.
Но, в любом случае, New-Dispose forever.
--
Regards, LVT.
← →
Leonid Troyanovsky © (2009-09-03 16:26) [8]
> Leonid Troyanovsky © (03.09.09 16:22) [7]
> > Я это понимаю, а что если MyRec глобальная?
Кста, глобальные инициализируются.
--
Regards, LVT.
← →
gosha52 (2009-09-03 16:32) [9]
> Но, в любом случае, New-Dispose forever.
В данном случае они не подходят, мне нужно просто очищать содержимое записи, не уничтожая MyRec.
Получается так, чтобы правильно очистить MyRec надо вызвать вначале Finalize(MyRec), а потом ZeroMemory(@MyRec), странно что не придумали одну функцию для выполнения этих действий. Хм.
← →
Anatoly Podgoretsky © (2009-09-03 16:53) [10]> gosha52 (03.09.2009 16:13:04) [4]
S := "";
← →
gosha52 (2009-09-03 17:31) [11]Еще один вопрос на этой волне :)
Изменю немного запись:
TMyRec = record
MyInt1: integer;
MyStr1: string;
MyStr2: string;
end;
PMyRec = ^TMyRec;
Если я теперь выделяю память для записи с помощью New, то все строки в записе будут пустыми или же их надо тоже чистить от мусора?, как к примеру с полем integer, который содержит в себе в большистве случаев не 0, пока не вызовишь ZeroMemory после New.
← →
Sapersky (2009-09-03 18:43) [12]Строки - очищает. System.pas:
function _New(size: Longint; typeInfo: Pointer): Pointer;
{$IFDEF PUREPASCAL}
begin
GetMem(Result, size);
if Result <> nil then
_Initialize(Result, typeInfo);
end;
_Initialize проходит по всем managed-полям (строки и т.п.) и заполняет их 0-ми. При статическом объявлении записи подобная функция (_InitializeRecord) автоматически вставляется компилятором при входе в область видимости переменной.
Почему не очищает всё? Ну, видимо, из соображений быстродействия. Запись - это относительно низкоуровневый тип, при работе с ней компилятор старается проявлять минимум "самодеятельности" (без очистки строк никак нельзя, они просто работать не будут). Если вам нужно, чтобы всё само очищалось - используйте классы.
← →
Anatoly Podgoretsky © (2009-09-03 18:57) [13]> gosha52 (03.09.2009 17:31:11) [11]
Зачем гадать, после создания задай начальные значения.
И забудь про ZeroMemory
← →
gosha52 (2009-09-03 22:39) [14]
> Sapersky (03.09.09 18:43) [12]
Примного благодарен Вам за подробное разъяснение. Я вроде и со стажем кодер, но такие моменты как видно упустил из виду, аж стыдно. Век живи, век учись :)
> Anatoly Podgoretsky © (03.09.09 18:57) [13]
У меня запись содержит в себе более десятка полей, некоторые из которых тоже записи. Слишком много кода будет чтобы задавать начальные значения каждый раз. Меня даже больше сам механизм работы с памятью интересовал.
← →
Anatoly Podgoretsky © (2009-09-03 23:08) [15]> gosha52 (03.09.2009 22:39:14) [14]
Ну тогда работай с неиниализироваными данными.
← →
Ruzzz (2009-09-04 11:49) [16]В Delphi 7 не было механизма отслеживающего утечки, поэтому не замечали. То что сказали по поводу разного размера структуры если есть строки, это не верно. Если задана строка string[N], то длина понятно будет такая, если задана просто строка то в экземпляре структуры будет хранится указатель на дин. память, где и будет ваша строка. Ну то есть так как и устроены строки :). При Zero вы затираете этот указатель нулями, и поэтому ваша структура уже не ссылается на него, и он как бы повис сам по себе - утечка. Потом когда вы присваиваете новое значение строке, и после выхода, уверен эта строка не числится в утечке.
← →
Ruzzz (2009-09-04 11:58) [17]Вообще мне самому интересен механизм работы компилятора и rtl но никак не изучу все это хорошо. Например здесь я уже задавал вопрос http://delphimaster.net/view/1-1251675285/ - там я говорил о методах Remove и Clear класса TStringHash из файла IniFiles, там не освобождается строки явно, и мне подсказали что Dispose сам все это делает. Но вот я столкнулся с проблемой, создал класс на основе TList, в списке храню структуры, в которой как и у вас есть строки не определенной длины (правда у меня AnsiString и Delphi 2006), так вот в переопределенном методе Notify приходится делать так:
procedure TRules.Notify(Ptr: Pointer; Action: TListNotification);
;
begin
if Action = lnDeleted then begin
with PRule(Ptr)^ do begin
SetLength(Name, 0);
SetLength(Filter, 0);
end;
Dispose(Ptr);
end;
inherited Notify(Ptr, Action);
end
то есть уповать на Dipose не получается иначе при выходе кричит об утечках, в дампе видно что это строки из этих структур. В чем проблема не знаю. Но после того как добавил SetLength проблема решилась.
Кстати если вы хотите инициализировать все нулями, то сперва освободите все строки и дин.массивы использует SetLength(Str, 0) или для строк Str := "" что одно и тоже.
← →
Ruzzz (2009-09-04 12:06) [18]По поводу _Initialize(даже _InitializeArray) - обящательно найдите в модуле system и исследуйте, это интересно :) и еще советую такой код помечать соот-щим комментарием, так как это не официально разрешенная функция, и вполне может быть что когда-то она изменится :). По поводу Finalize, есть описание в справке, и еще можно посмотреть TStringList.Delete из SysUtils
← →
Ruzzz (2009-09-04 12:15) [19]Эх, я тут расписал а судя по вашей фразе
> Получается так, чтобы правильно очистить MyRec надо вызвать
> вначале Finalize(MyRec), а потом ZeroMemory(@MyRec), странно
> что не придумали одну функцию для выполнения этих действий.
> Хм.
вы уже разобрались, да получается что Dispose вызывает Finalize и FreeMem, и если вам не нужно удалять свою структу то просто вызвать Finalize перед ZeroMemory, но вот у меня почему то этот Finalize в Dispose не срабатывает, точнее не дает эффекта, приходится вручную иначе сообщение об утечке. Понимаю что это я что-то делаю не так, но что не пойму.
← →
Leonid Troyanovsky © (2009-09-04 12:20) [20]
> Ruzzz (04.09.09 12:15) [19]
> что это я что-то делаю не так, но что не пойму.
Dispose(PRule(Ptr));
--
Regards, LVT.
← →
Ruzzz (2009-09-04 13:19) [21]Вот оно! Спасибо! :)
← →
Sapersky (2009-09-04 14:18) [22]Чтобы _Finalize сработал правильно - ему нужен правильный TypeInfo, а его компилятор подставляет по типу передаваемой переменной.
Ну и вот ещё некое дополнение к RTL"ным функциям вроде _Initialize/_Finalize (работает по тому же принципу):
http://sapersky.narod.ru/files/TypInfoEx.rar
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2011.04.10;
Скачать: [xml.tar.bz2];
Память: 0.54 MB
Время: 0.007 c