Delphi 这里有内存泄漏吗?
这段代码可以防止内存泄漏吗Delphi 这里有内存泄漏吗?,delphi,memory,optimization,memory-management,memory-leaks,Delphi,Memory,Optimization,Memory Management,Memory Leaks,这段代码可以防止内存泄漏吗 s := TStringList.Create; // create first object try // Here line comes that seems to be dangerous s := GetSomeSettings; // Overrides reference to first object by second one finally s.free; // Destroying only second object, leave
s := TStringList.Create; // create first object
try
// Here line comes that seems to be dangerous
s := GetSomeSettings; // Overrides reference to first object by second one
finally
s.free; // Destroying only second object, leave first object to live somewhere in memory
end;
function GetSomeSettings : TStringList;
var
rawString : string;
settings : TStringList;
begin
// Singleton pattern implementation
// Trying to find already existing settings in class variable
settings := TSettingsClass.fSettings;
// If there is no already defined settings then get them
if not Assigned(settings) then
begin
GetSettingsInDB(rawString);
TSettingsClass.fSettings := ParseSettingsString(rawString);
settings := TSettingsClass.fSettings;
end;
Result := settings;
end;
我想知道
s:=GetSomeSettings代码>潜在有害并忽略第一个对象,将其保留在内存中?是的,在第1行创建的StringList泄漏
基本上,你正在做:
s := TStringList.Create;
s := AnotherStringList;
AnotherStringList.Free;
对于GetSomeSettings
例程:
通常,将新创建的实例作为函数结果返回是不明智的,因为您将所有权和销毁的责任转移给调用代码。除非你有一个适当的机制/框架来处理它,就像你的TSettingsClass
一样,但是在这段代码中没有足够的证据
然而,这两段代码的组合显示出另一个问题:在s.Free
之后,TSettingsClass.fSettings
会被销毁,但不会为零。因此,第二次调用GetSomeSettings
时,它返回一个悬空指针。1)您不应该询问两分钟后什么时候可以检查
program {$AppType Console};
uses Classes, SysUtils;
type TCheckedSL = class(TStringList)
public
procedure BeforeDestruction; override;
procedure AfterConstruction; override;
end;
procedure TCheckedSL.BeforeDestruction;
begin
inherited;
WriteLn('List ',IntToHex(Self,8), ' going to be safely destroyed.');
end;
procedure TCheckedSL.AfterConstruction;
begin
WriteLn('List ',IntToHex(Self,8), ' was created - check whether it is has matched destruction.');
inherited;
end;
procedure DoTest; var s: TStrings;
function GetSomeSettings: TStrings;
begin Result := TCheckedSL.Create end;
begin
Writeln('Entered DoTest procedure');
s := TCheckedSL.Create; // create first object
try
// Here line comes that seems to be dangerous
s := GetSomeSettings; // Overrides reference to first object by second one
finally
s.free; // Destroying only second object, leave first object
end;
Writeln('Leaving DoTest procedure');
end;
BEGIN
DoTest;
Writeln;
Writeln('Check output and press Enter when done');
ReadLn;
END.
2) 不过,在少数特殊情况下,这可能是安全的
在FPC中()S
可能是某个单元的“全局属性”,具有释放旧列表的setter
在Delphi Classic中,S
可以是某种接口类型,由创建的对象支持。诚然,标准TStringList
缺少任何接口,但一些库(例如)确实提供了基于接口的字符串列表,具有更丰富的API(iJclStringList
类型和相关)
在Delphi/LLVM中,所有对象都进行了引用计数,就像没有GUID的接口一样。这样代码在那里就安全了
您可以将S
声明为记录-所谓的Extended record
重新定义了class操作符Implicit
,以便typecastS{record}:=TStringList.Create
将在分配新实例之前释放以前的实例。但这是很危险的,因为它非常脆弱,很容易被误用,并在其他地方销毁列表,在S
记录中留下一个悬空的指针
您的对象可能不是普通的TStringList
,而是一些子类,覆盖构造函数或AfterConstruction
,将自己注册到某个列表中,这将在某个地方同时完成。围绕大量工作负载的Mark/Sweep
堆管理。VCLTComponent可能被视为遵循以下模式:表单拥有它的组件,并在需要时释放它们。这就是你用TSettingsClass.fSettings
container(任何引用都是1号容器)试图做的事情。然而,这些框架确实需要一个环回:当对象被释放时,它也应该从所有容器中移除自己,引用它
只需将第一块代码替换为s:=GetSomeSetting为什么不检查内存泄漏?您可以使用FastMM,或者如果您的Delphi版本支持,则在应用程序启动时,ReportMemoryLeaksOnShutdown:=True当应用程序关闭时,会显示任何内存泄漏。正如其他人所指出的,这将报告x1TStringList
对象泄漏。答案详尽且信息丰富。至少我学到了一些东西+1.
procedure TCheckedSL.BeforeDestruction;
begin
if Self = TSettingsClass.fSettings then TSettingsClass.fSettings := nil;
inherited;
end;
class procedure TSettingsClass.SetFSettings(Value);
var fSet2: TObject;
begin
if fSettings <> nil then begin
fSet2 := fSettings;
f_fSettings := nil; // breaking the loop-chain
fSet2.Destroy;
end;
f_fSettings := Value;
end;
class destructor TSettingsClass.Destroy;
begin
fSettings := nil;
end;
procedure TCheckedSL.AfterConstruction;
begin
inherited;
TSettingsClass.fSettings := Self;
end;
...
if not Assigned(settings) then
begin
GetSettingsInDB(rawString);
TCheckedSL.Create.Text := ParseSettingsString(rawString);
settings := TSettingsClass.fSettings;
Assert( Assigned(settings), 'wrong class used for DB settings' );
end;
Result := settings;