使用ToBytes方法(见下文)将AnsiString的硬类型转换替换为TBytes(字符串数组)后,Delphi报告没有内存泄漏 - 但是,如果将TBytes值传递给a,则会显示泄漏。参数类型为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
如果我将Send方法更改为采用TBytes类型的参数,则内存泄漏将消失。
答案 0 :(得分:4)
这是一个编译器错误。托管类型TBytes
具有引用计数生命周期。编译器应该创建一个隐式局部变量,该变量被赋予ToBytes
返回的数组。您需要通过存储到显式本地来解决这个问题:
var
Tmp: TBytes;
....
Tmp := ToBytes(...);
Send(Tmp);
答案 1 :(得分:1)
这可能不是你认为的错误。 FPC的堆跟踪已知在主程序(主.dpr begin..end)中跟踪临时值(以及一般的自动类型)的问题。
将代码移动到一个过程,并从主begin..end调用它。你会看到泄漏消失了。
这是因为主程序的一般结构就像
begin
initializeunits(); // procedure call inserted by the compiler
<actual mainprogram statements>
finalizeunits(); // procedure call inserted by the compiler
end.
释放主程序临时发生在“结束”。在finalizeunits完成堆栈跟踪之后。 (即使它是第一单位,它仍然只是一个单位)。所以heaptrc错过了。
答案 2 :(得分:0)
返回可能包含大量内存的东西的函数让我感到娇气。这可能只是一种感觉或个人偏好,但它确实消除了对内存分配的控制。关于返回(新构造的)TStringList实例的函数也有类似的事情,也建议将指针传递给TStrings对象并让调用者控制对象的生命周期。
在这里,我建议对TBytes上的所有操作使用var参数,并强制调用者提供一个实例来处理。如果您构建了一个精心设计的应用程序并且将必须搜索可以提高性能的位置,请再次考虑这一点,看看是否可以回收具有类似内容或长度的TBytes实例。 Delphi字符串系统的一个重要特性是在幕后为您进行引用计数和写时复制,在您的应用程序处理大量(类似)字符串时提供这种性能提升。