2014-03-04 2 views
7

После замены жестких типов приведения AnsiString в TBytes (массив строки) с помощью метода ToBytes (см. Ниже) Delphi сообщила об отсутствии утечек памяти - Free Pascal 2.6.2 однако показывает утечку в случае, если значение TBytes передается методу с параметром типа Pointer.Ошибка утечки памяти в Free Pascal, вызванная методами с параметром 'pointer'

Следующий код утечки памяти: отчет утечки

program project1; 

{$mode delphi} 

uses 
    SysUtils; 

function ToBytes(const AValue: AnsiString): TBytes; 
begin 
    SetLength(Result, Length(AValue)); // <-- leak (ine 10) 
    if Length(AValue) > 0 then 
    Move(AValue[1], Result[0], Length(AValue)); 
end; 

procedure Send(P: Pointer); 
begin 

end; 

begin 
    Send(ToBytes('test')); 

    SetHeapTraceOutput('heaptrace.log'); 
end. 

Память:

Call trace for block $001C5CC0 size 12 $00401586 TOBYTES, line 10 
of project1.lpr $00401622 main, line 21 of project1.lpr 

Если изменить метод отправки принять аргумент терабайт типа, утечка памяти исчезает.

+0

Зарегистрировано в Free Pascal/Lazarus Bug Tracker http://bugs.freepascal.org/view.php?id=25825 – mjn

ответ

4

Это ошибка компилятора. Управляемый тип TBytes имеет подсчитанное время жизни. Компилятор должен создать неявную локальную переменную, которой присваивается массив, возвращаемый ToBytes. Вы должны будете работать вокруг этого путем сохранения в явное локальное:

var 
    Tmp: TBytes; 
.... 
Tmp := ToBytes(...); 
Send(Tmp); 
+0

Что-то не так с компилятором. Если я удалю декларацию TBytes и использую SysUtils (там, где она определена), я получаю SIGSEGV на 'end.' –

+0

@StefanGlienke. Я попытался перевести код из верхнего уровня' begin end.' в функцию и получил SIGSEV при запуске процесса. Weird. –

+0

Кажется, что-то связано с блоком heaptrc. 'program project1; использует SysUtils, heaptrc; begin end. '=> SIGSEGV –

0

функции, которые возвращают вещи, которые потенциально содержат много памяти делают меня брезгливый. Это может быть просто чувство или личное предпочтение, но оно убирает контроль над распределением памяти. Аналогичные вещи говорят о функции, возвращающей (только что построенные) экземпляры TStringList, там также желательно передать указатель на объект TStrings и позволить вызывающему абоненту контролировать время жизни объекта.

Здесь я бы посоветовал использовать параметр var для всех операций с TBytes и заставить вызывающего пользователя предоставить экземпляр для работы. Если вы создадите сложное приложение и , то должны искать, где вы можете повысить производительность, подумайте об этом еще раз и посмотрите, можете ли вы перерабатывать экземпляры TBytes с похожим контентом или длиной. Важной особенностью строковой системы Delphi является то, что вы делаете подсчет ссылок и копирование на запись за кулисами, обеспечивая это увеличение производительности, когда ваше приложение обрабатывает много (похожих) строк.

+0

Это скорее комментарий, чем ответ на заданный вопрос. Вы концентрируетесь на одном вопросе, а именно на показателях производительности менее оптимальных распределений кучи. Есть еще одна проблема, и это одна из возможностей. Функция, возвращающая значение, может использоваться как аргумент другой функции. Или в правой части оператора присваивания. С другой стороны, процедура, возвращаемая параметром 'var', требует нескольких отдельных операторов и назначений локальным переменным, которые в противном случае не нужны. TBC –

+0

Итак, если вас беспокоит читаемость кода и ясность, тогда возвращаемые значения всегда предпочтительнее, где они практичны. В качестве примера у меня есть число числовых типов в моей базе кода. Одним из сильно используемых типов является вектор с 3 действительными элементами. Это объявлено как запись, многие функции возвращают его, и я широко использую перегрузку оператора. Вы используете его так же, как и встроенный тип типа «Целое», «Двойной», «Строковый» и т. Д. Код всегда кажется правильным. TBC –

+0

С другой стороны, у меня есть векторный тип, размер которого известен только во время выполнения. Это означает, что элементы должны жить в куче. И их может быть очень много. Таким образом, производительность становится реальной проблемой. Я действительно не хочу выполнять больше распределений кучи, чем нужно. Для этого класса методы, которые работают с типом, передаются готовыми экземплярами, создаваемыми вызывающим. Это отвечает потребностям производительности, но код не читается! Итак, лошади для курсов. –

1

Возможно, это не ошибка, где вы думаете. Трассировка кучи FPC знала проблемы с темпами отслеживания (и автоматическими типами вообще) в основных программах (основной .dpr begin..end).

Переведите код в процедуру и вызовите это из основного начала ..end. и вы увидите, что утечка исчезнет.

Это потому, что общая структура основной программы, как

begin 
    initializeunits(); // procedure call inserted by the compiler 
    <actual mainprogram statements> 
    finalizeunits(); // procedure call inserted by the compiler 
end. 

с отпусканием mainprogram TEMPS происходит в «конце.» после finalizeunits, который завершает heaptracking. (даже если это первый блок, это все еще только единица). Поэтому heaptrc пропускает это.

+0

Перемещение кода на процедуру и вызов ее из главного begin ... end не устраняет утечку (я добавил комментарий с сообщением об утечке в элемент отслеживания ошибок). – mjn

+0

Кажется, действительно проблема в исправлениях, которые уже решены в багажнике. Я перекомпилировал с 2.6.x и получил один memleak и 0 с 2.7.1. Если я добавлю a для него (с 2.6.x), у меня будет 10 утечек. Довольно серьезно. –