我的问题是关于调试内存泄漏,这似乎是一场噩梦.
在我的应用程序中有一个简单的类派生自TObject
.该类的所有对象都存储在派生自TObjectList
以下类的类的集合/列表中:
type TOffer = class(TObject) Item: string; Price: string; Id: string; end; TOffers = class(TObjectList) protected procedure SetOffer(I: Integer; AOffer: TOffer); function GetOffer(I: Integer): TOffer; public property Offers[I: Integer]: TOffer read GetOffer write SetOffer end;
使用场景:
爬虫下载商品,解析商品并保存到对象集合.这种方法似乎很方便,因为我可以稍后引用这些对象(填充网格/列表,将它们写入文件等)
问题是正确处理对象以避免内存泄漏.该应用程序在启动时分配~4Mb内存,但在处理后~12k提供它吞噬32Mb.在过程完成后由未正确处理的对象/变量引起的泄漏.
ReportMemoryLeaksOnShutdown
显示可怕的数字,但关键是 - 我不知道在哪里看,以及如何正确调试该死的东西.
另一个例子是var MyString: string
需要适当处置的变量!! 这对我来说很有见地:)我认为每个过程/函数都会自动管理超出范围的变量的垃圾收集.
商品列表由函数创建:
function GetOffersList: TOffers; begin Result := TOffers.Create; while not rs.EOF do begin Offer := TOffer.Create; try // here come collected offer attributes as variables of type string: Order.Item := CollectedOfferItem; Order.Price := CollectedOfferPrice; Order.Id := CollectedOfferId; Result.Add(Offer); finally Offer := nil; end; end; end;
然后我直接将这些优惠作为一个集合来解决.关键是我希望这个应用程序全天候运行,因此必须正确处理资源.
如何妥善处理上述类型的对象?
我应该考虑管理对象/对象列表的其他技术吗?
如何正确处理类型的变量string
?
你能否告诉Delphi中有关打击内存泄漏的好读物?
谢谢.
默认情况下,当您创建对象时,您将成为其所有者.只要您是所有者,您就有责任将其释放.以下是一些常见模式:
1.局部变量
对于在方法中创建并仅在本地引用的对象,可以使用try/finally模式:
Obj := TMyClass.Create; try ... use Obj finally Obj.Free; end;
2.另一个对象拥有的对象
通常在构造函数中创建并在析构函数中销毁.这里有一个拥有对象的成员字段,用于保存对拥有对象的引用.您需要做的就是调用Free
拥有类析构函数中的所有拥有对象.
3.拥有的TComponent
如果使用TComponent
a创建了一个或派生类Owner
,则该所有者会销毁该组件.你不需要.
4. TObjectList或类似的OwnsObjects设置为True
您在问题中显示此模式.你创建一个TObjectList
默认OwnsObjects
是True
.这意味着当您向容器添加成员时,容器将承担所有权.从那时起,集装箱承担了销毁其成员的责任,而你不必这样做.然而,有人仍然需要销毁容器.
5.引用计数接口对象
常见的例子是派生自的对象TInterfacedObject
.接口引用计数管理生命周期.您不需要销毁该对象.
6.创建并返回新实例的函数
这是针对更棘手的频谱结束.值得庆幸的是,这是一种相当罕见的模式.这个想法是该函数将一个新实例化和初始化的对象返回给调用者,然后调用者承担所有权.但是当函数仍在执行时,它是所有者并且必须防止异常.通常代码如下:
function CreateNewObject(...): TMyClass; begin Result := TMyClass.Create; try Result.Initialize(...); except Result.Free; raise; end; end;
这必须是一个异常处理程序,它有一个调用Free
和重新加注,因为代码不能使用finally.来电者会这样做:
Obj := CreateNewObject(...); try .... finally Obj.Free; end;
查看问题中的代码,似乎使用了我的列表中的第4项和第6项.但是,请注意您的实现GetOffersList
不是例外安全.但没有迹象表明这是问题所在.调用的代码GetOffersList
无法破坏容器似乎是合理的.
你为什么要漏字?那么,字符串是托管对象.它们被引用计数,您不需要采取任何明确的行动来销毁它们.但是,如果它们包含在其他类中,其实例被泄露,则包含的字符串也会泄露.因此,集中精力修复物体的泄漏,你将处理字符串泄漏.
对于它来说,TOffer
感觉更像是一种价值类型而不是一种参考类型.它没有方法,包含三个简单的标量值.为什么不把它作为记录并使用TList
?
那么,你如何进行?FastMM泄漏报告是您所需要的.你需要完整的FastMM而不是减少Embarcadero版本.它将识别与解除分配不匹配的分配.一个接一个地处理它们.
与此同时,研究高质量的代码.好的开源Delphi库将展示上面的所有模式,以及更多.向他们学习.