MadExcept报DynamicArray内存泄漏,求解析kbmMemTable相关代码问题
为什么MadExcept会报告Delphi动态数组内存泄漏?
我正在用MadExcept检测Delphi程序的内存泄漏,对以下报告的动态数组泄漏存在疑问:
MadExcept泄漏报告详情
allocation number: 526920 program up time: 21.98 s type: DynamicArray address: $211e4d00 size: 28 access rights: read/write reference counter: 1 item count: 5 item size: 4 main thread ($6d9c): 671a7cc7 madExcept32.dll madExceptDbg 1746 GetMemCallback 00d8765f QX.exe System 4818 @ReallocMem 00d904ca QX.exe System 4818 DynArraySetLength 00d90631 QX.exe System 4818 @DynArraySetLength 01b7d2a3 QX.exe kbmMemTable 17393 TkbmCustomStreamFormat.DetermineSaveFields 00ec42f5 QX.exe System.Classes TStream.SetPosition 01b7d7f6 QX.exe kbmMemTable 17509 TkbmCustomStreamFormat.BeforeSave 0264fcb9 QX.exe kbmMemCSVStreamFormat 560 TkbmCustomCSVStreamFormat.BeforeSave 01b75126 QX.exe kbmMemTable 14331 TkbmCustomMemTable.InternalSaveToStreamViaFormat 01b74e97 QX.exe kbmMemTable 14272 TkbmCustomMemTable.SaveToFileViaFormat 01b74eb3 QX.exe kbmMemTable 14274 TkbmCustomMemTable.SaveToFileViaFormat 01b75a7c QX.exe kbmMemTable 14571 TkbmCustomMemTable.SavePersistent 01b75b80 QX.exe kbmMemTable 14591 TkbmCustomMemTable.SavePersistent 00d8760c QX.exe System 4818 @FreeMem 01b6ba03 QX.exe kbmMemTable 10523 TkbmCustomMemTable.SetActive 010c94c4 QX.exe Data.DB TDataSet.Close 010b7432 QX.exe Data.DB TField.Destroy 00d8a930 QX.exe System 4818 TObject.Free 00ed94be QX.exe System.Classes TComponent.DestroyComponents 00ed8f47 QX.exe System.Classes TComponent.Destroy 00f6be75 QX.exe Vcl.Controls TControl.Destroy 00e3e6b9 QX.exe madExcept InterceptClassDestroy 00ec05df QX.exe System.Classes TPersistent.Destroy 00f70c51 QX.exe Vcl.Controls TWinControl.Destroy 01060b13 QX.exe Vcl.Forms TScrollingWinControl.Destroy 010620e1 QX.exe Vcl.Forms TCustomForm.Destroy 01062100 QX.exe Vcl.Forms TCustomForm.Destroy 00d8a930 QX.exe System 4818 TObject.Free 0274b08b QX.exe ReportAdmin 4057 CDS_CheckAndImportReportFiles 043b1673 QX.exe Reports 184 TReports.LoadReports 044257a1 QX.exe QX.Startup.Common 1174 StartupCode 0448568b QX.exe QX 638 initialization memory dump: 211e4d00 01 00 00 00 05 00 00 00 - 00 00 00 00 01 00 00 00 ................ 211e4d10 02 00 00 00 03 00 00 00 - 04 00 00 00 ............
对应的代码片段
泄漏指向TkbmCustomStreamFormat.DetermineSaveFields方法中的SetLength(SaveFields,nf);语句:
procedure TkbmCustomStreamFormat.DetermineSaveFields(ADataset:TkbmCustomMemTable); var i:TkbmNativeInt; nf:TkbmNativeInt; begin // Setup flags for fields to save. with ADataset do begin nf:=Fieldcount; SetLength(SaveFields,nf); for i:=0 to nf-1 do begin // Default dont save this field. SaveFields[i]:=-1; // If a blob field, only save if specified. if (Fields[i].DataType in kbmBlobTypes) then begin if not (sfSaveBlobs in sfBlobs) then continue; SaveFields[i]:=i; end; // Only save fields of specific types. case Fields[i].FieldKind of fkData,fkInternalCalc: if sfSaveData in sfData then SaveFields[i]:=i; fkCalculated: if sfSaveCalculated in sfCalculated then SaveFields[i]:=i; fkLookup: if sfSaveLookup in sfLookup then SaveFields[i]:=i; else SaveFields[i]:=-1; end; // If not to save invisible fields, dont. if not (Fields[i].Visible or (sfSaveNonVisible in sfNonVisible)) then SaveFields[i]:=-1; end; end; end;
泄漏原因分析
你说的没错,Delphi局部动态数组确实会在超出作用域时自动通过引用计数机制释放,但这里的SaveFields并不是局部变量——它是TkbmCustomStreamFormat类的成员变量。
成员动态数组的生命周期和类实例绑定:
- 当你在
DetermineSaveFields中调用SetLength(SaveFields,nf)时,会分配内存并增加数组的引用计数 - 方法执行完毕后,成员变量
SaveFields仍然持有该数组的引用,引用计数不会归零 - 如果
TkbmCustomStreamFormat实例没有被正确销毁,或者在实例销毁前没有显式清空SaveFields(比如调用SetLength(SaveFields, 0)或赋值为nil),数组的内存就不会被释放 - MadExcept报告显示该数组的引用计数为1,正好说明存在一个未被释放的活跃引用,就是
SaveFields成员变量
要解决这个问题,需要检查TkbmCustomStreamFormat类的销毁逻辑,确保在Destroy方法中清空SaveFields数组,或者在使用完数组后立即重置它。
内容的提问来源于stack exchange,提问作者Frank Pedro
相关产品推荐
相关产品推荐

