Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Утечка памяти при пошаговой отладке


Автор: bems 9.5.2009, 02:14
Здравствуйте !
Допустим, у нас есть такой код:
Код

program ToLeakOrNotToLeak;
{$APPTYPE CONSOLE}

uses
  SysUtils;

type TClss = class
             strict private
              function GetString: string;
             public
              property Str: string read GetString;
             end;

function TClss.GetString: string;
begin
Result := 'string'
end;

var Obj: TClss;
begin
  try
    ReportMemoryLeaksOnShutdown := True;
    Obj := TClss.Create;
    try
     writeln(Obj.Str);
    finally
     Obj.Free
    end;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.


Он нормально работает и рапорта об утечках памяти не выдает.
Теперь попробуем его пройти таким образом:
Ставим брякпоинт на строке writeln(Obj.Str)
Запускаем программу, дебагер останавливается на указаной строке.
Наводим мышь на "Str" в этой строке и дожидаемся появления хинта со значением свойства Str.
Жмем F9.

В результате получаем сообщение об утечке памяти.
Наблюдается в CodeGear™ Delphi® 2009  Version 12.0.3210.17555 (то есть с первым и вторым апдейтами)

Подскажите, пожалуйста, с чем это может быть связано, как с этим бороться
и как мне в будущем отличать настоящие утечки от "глючных" (если конечно это глюк среды) ?

Автор: CodeMonkey 9.5.2009, 13:27
Глюк среды, однозначно. Странно, что он возникает при чтении. А есть поставить FastMM в FullDebugMode, куда будет вести CallStack? (опции Use Debug DCUs, Stack Frames лучше бы включить)

Собственно отладчик довольно активно участвует в работе программы. Он может и менять её память и вызывать функции менеджера памяти. Это довольно очевидно, хотя об этом мало кто помнит. Например: когда вы меняете строковую переменную, то отладчик не может же просто взять и изменить память под данные строки, т.к. длина строки может измениться. Он вынужден выделить новую строчку GetMem-ом, а старую освободить FreeMem-ом. И всё это, пока программа стоит на паузе! С её точки зрения это довольно удивительно.

В данном конкретном случае отладчик вынужден сделать вызов функции, чтобы узнать значение строки. Хотя в данном конкретном случае никакого выделения памяти происходить не может: мы ведь констранту возвращаем.

Добавлено через 14 минут и 41 секунду
Посмотрел сам.

Вот что получилось:

Код
This block was allocated by thread 0x1C68, and the stack trace (return addresses) at the time was:
405843 [System.pas][System][System.@NewUnicodeString][18113]
4052CC [System.pas][System][System.@UStrAsg][16885]
40F24F [Project8.dpr][Project8][Project8.TClss.GetString][17]
20036 
77674911 [BaseThreadInitThunk]
77C8E4B6 [Unknown function at RtlInitializeExceptionChain]
77C8E489 [Unknown function at RtlInitializeExceptionChain]


0x1C68 - это главный поток.
А вот 20036 - это хз кто. Видать, отладчик. Похоже, новая строка всё же создаётся (вижу вызов NewUnicodeString). Хотя неясно зачем.

Автор: CodeMonkey 9.5.2009, 14:02
Посмотрим, что скажут: http://qc.embarcadero.com/wc/qcmain.aspx?d=73762.

Автор: bems 9.5.2009, 15:05
Но почему выделяется память под константу, это половина вопроса. А другая половина: а почему бы дебаггеру ее потом не освободить?

Автор: Mechanic 14.5.2009, 05:15
Цитата(CodeMonkey @  9.5.2009,  14:02 Найти цитируемый пост)
Посмотрим, что скажут: QC Report #73762. 


Цитата

 Workarounds
Don't use memory leak detection while debugging.


Аглицкие мастера, поди, брэйк/трейсом мемлики не ловют.. smile

Автор: MetalFan 14.5.2009, 08:18
Mechanic, так я понимаю это CodeMonkey и написал

Автор: Mechanic 14.5.2009, 08:51
MetalFan, понял то же, но позже.. Сбило с толку слово Workaround.  smile 

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

Автор: bems 14.5.2009, 17:58
Цитата(Mechanic @  14.5.2009,  08:51 Найти цитируемый пост)
Имхо, мемлик - это вещь, для отлова которой не нужен трейс, достаточно и тестовых прогонов. 
Да, но именно для отлова. Когда мы знаем что он есть и ловим его. А когда мы отлаживаем код в котором не замечено утечек памяти и вдруг видим сообщение, то начинаем искать ситуацию ,когда лик повторяется. И в процессе этого поиска не пользуемся отладчиком, поэтому лик не повторяется. Мы в шоке, потому что ищем то чего нет. После этого моего топика мы знаем что отладчик имеет побочный эффект и теперь можем не искать то чего нет, чтоб доказать что его не существует. Решает ли это проблему? Нет, только создаёт. Теперь если в программе при редких условиях течёт память, и мы помним что в текущем сеансе пользовались дебагером, то мы списываем все на него, а лик благополучно остается в коде.

Автор: Mechanic 18.5.2009, 09:25
Во намудрил..  smile 
Понятно, что плохо без любой доп. возможности. Не есть хорошо, когда что-то не работает.
Просто после этой статьи, мне например, не придёт в голову гоняться с трейсом за призраками.
Лучше уж я лишний OutputDebugMessage вставлю, не развалюсь.

Посему, за труд, и за сигнал - поклон вам, bems & CodeMonkey!
И крепких нам всем нервов, и.. Удачной Охоты!  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)