Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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.005 c
10-1173096821
skosenok
2007-03-05 15:13
2011.04.10
Разные ServerName для двух Class-ов в одной ActiveX


8-1211705863
lamer666
2008-05-25 12:57
2011.04.10
Видео чат!


9-1186778469
Gari
2007-08-11 00:41
2011.04.10
Проигрование звука в игре


2-1294760444
polyaev
2011-01-11 18:40
2011.04.10
Прошу помощи в задании


15-1293210445
asd
2010-12-24 20:07
2011.04.10
чо бы почитать для начинающего пользователя





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский