2017-06-19 38 views
0

我正在創建一個新的組件,它從TClientDataSet繼承,並導致內存泄漏。我創建了一個演示來重現錯誤(不需要共享myCustomComponent)。我該如何解決這個內存泄漏問題?TClientDataSet CreateDataset內存泄漏

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    ClientDataSet1.Close; 
    ClientDataSet1.Tag := 1; 
    ClientDataSet1.Open; 
end; 

procedure TForm1.ClientDataSet1BeforeOpen(DataSet: TDataSet); 
begin 
    if ClientDataSet1.Tag = 1 then 
    begin 
     ClientDataSet1.Tag := 0; 
     ClientDataSet1.Fields.Clear; 
     ClientDataSet1.FieldDefs.Clear; 
     ClientDataSet1.FieldDefs.Add('Collection', ftString, 50); 
     ClientDataSet1.FieldDefs.Find('Collection').CreateField(ClientDataSet1); 
     ClientDataSet1.CreateDataSet; 
    end; 
end; 

FastMM事件日誌文件:

--------------------------------2017/6/19 12:00:21-------------------------------- 
A memory block has been leaked. The size is: 68 

This block was allocated by thread 0x4FA8, and the stack trace (return addresses) at the time was: 
418CD6 [FastMM4.pas][FastMM4][DebugAllocMem$qqri][9900] 
407276 [System.pas][System][AllocMem$qqri][4557] 
65F91B [Datasnap.DBClient.pas][Datasnap.DBClient][Dbclient.TCustomClientDataSet.AllocKeyBuffers$qqrv][3965] 
6598AE [Datasnap.DBClient.pas][Datasnap.DBClient][Dbclient.TCustomClientDataSet.InternalOpen$qqrv][1514] 
623BC8 [Data.DB.pas][Data.DB][Db.TDataSet.DoInternalOpen$qqrv][12527] 
623C77 [Data.DB.pas][Data.DB][Db.TDataSet.OpenCursor$qqro][12556] 
65885B [Datasnap.DBClient.pas][Datasnap.DBClient][Dbclient.TCustomClientDataSet.OpenCursor$qqro][1282] 
623B2F [Data.DB.pas][Data.DB][Db.TDataSet.SetActive$qqro][12508] 
62396B [Data.DB.pas][Data.DB][Db.TDataSet.Open$qqrv][12464] 
65BE16 [Datasnap.DBClient.pas][Datasnap.DBClient][Dbclient.TCustomClientDataSet.CreateDataSet$qqrv][2342] 
706827 [Unit1.pas][Unit1][TForm1.ClientDataSet1BeforeOpen$qqrp16Data.Db.TDataSet][64] 

The block is currently used for an object of class: Unknown 

The allocation number is: 1092 

Current memory dump of 256 bytes starting at pointer address 7EF776F0: 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F1 7F B4 7C 
80 80 80 80 80 80 80 80 00 00 00 00 C1 70 F7 7E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
73 04 00 00 D6 8C 41 00 76 72 40 00 1B F9 65 00 AE 98 65 00 C8 3B 62 00 77 3C 62 00 5B 88 65 00 
2F 3B 62 00 6B 39 62 00 F4 65 70 00 D5 A7 53 00 A8 4F 00 00 A8 4F 00 00 AA 72 40 00 BE F5 60 00 
AF F9 65 00 FA 9A 65 00 B8 3C 62 00 06 8C 65 00 9C 3B 62 00 F7 31 62 00 3C 8B 41 00 51 8B 41 00 
29 7A 65 00 3C 00 00 00 00 00 00 00 69 F3 D9 86 E4 FB 71 00 80 80 80 80 80 80 80 80 80 80 80 80 
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . ñ ´ | 
€ € € € € € € € . . . . Á p ÷ ~ . . . . . . . . . . . . . . . . 
s . . . Ö Œ A . v r @ . . ù e . ® ˜ e . È ; b . w < b . [ ˆ e . 
/; b . k 9 b . ô e p . Õ § S . ¨ O . . ¨ O . . ª r @ . ¾ õ ` . 
¯ ù e . ú š e . ¸ < b . . Œ e . œ ; b . ÷ 1 b . < ‹ A . Q ‹ A . 
) z e . < . . . . . . . i ó Ù † ä û q . € € € € € € € € € € € € 
€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € 

當從fastMM試圖@victoria的代碼我得到以下錯誤(althoug ReportMemoryLeaksOnShutDown不會引發任何東西),然後我意識到FastMM搞糊塗了。我已經重新引入Open方法並將此代碼移至此處(不調用繼承或打開),ReportMemoryLeaksOnShuntdown顯示沒有泄漏,但我無法理解爲什麼FastMM會引發。

enter image description here

A memory block has been leaked. The size is: 68 

This block was allocated by thread 0x4918, and the stack trace (return addresses) at the time was: 
4DB29E54 
4DB28419 
4DB21AA9 
4DB1FB5B 
7711F11C [VirtualQuery] 
33B8FD9 [GetFrameBasedStackTrace] 
33B901C [GetFrameBasedStackTrace] 
77983431 [Unknown function at RtlQueryPerformanceCounter] 
33B9336 [GetRawStackTrace] 
417A0E [FastMM4.pas][FastMM4][CalculateHeaderCheckSum$qqrp29Fastmm4.TFullDebugBlockHeader][9080] 
417A1D [FastMM4.pas][FastMM4][UpdateHeaderAndFooterCheckSums$qqrp29Fastmm4.TFullDebugBlockHeader][9090] 

The block is currently used for an object of class: Unknown 

The allocation number is: 1060 

Current memory dump of 256 bytes starting at pointer address 7EF76FA0: 
3C 00 B4 4D 08 00 00 00 00 00 00 00 00 00 00 00 3F 00 00 00 00 80 80 80 80 80 80 80 80 80 80 80 
80 80 80 80 80 80 80 80 43 00 80 80 80 80 80 80 80 80 80 80 80 80 80 80 01 00 00 00 0F 00 00 00 
8C A3 AB 60 80 80 80 80 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
45 04 00 00 E8 A1 40 00 A3 AC 40 00 B5 F6 40 00 A0 BD 65 00 79 68 70 00 D5 A7 53 00 47 1F 55 00 
55 2A 55 00 65 A2 53 00 38 70 3F 75 DD B4 04 74 18 49 00 00 18 49 00 00 AA 72 40 00 85 A2 40 00 
3B BE 65 00 79 68 70 00 D5 A7 53 00 47 1F 55 00 55 2A 55 00 65 A2 53 00 38 70 3F 75 DD B4 04 74 
89 B5 04 74 1B 00 00 00 E9 FD 01 00 04 D3 19 CB E4 FB 71 00 80 80 80 80 80 80 80 80 80 80 80 80 
80 80 80 80 80 80 80 80 80 80 80 FB 2C E6 34 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 
< . ´ M . . . . . . . . . . . . ? . . . . € € € € € € € € € € € 
€ € € € € € € € C . € € € € € € € € € € € € € € . . . . . . . . 
Œ £ « ` € € € € . . . . . . . . . . . . . . . . . . . . . . . . 
E . . . è ¡ @ . £ ¬ @ . µ ö @ .   ½ e . y h p . Õ § S . G . U . 
U * U . e ¢ S . 8 p ? u İ ´ . t . I . . . I . . ª r @ . … ¢ @ . 
; ¾ e . y h p . Õ § S . G . U . U * U . e ¢ S . 8 p ? u İ ´ . t 
‰ µ . t . . . . é ı . . . Ó . Ë ä û q . € € € € € € € € € € € € 
€ € € € € € € € € € € û , æ 4 € € € € € € € € € € € € € € € € € 
+0

貌似數據集被打開兩次。當你遇到堆棧溢出導致你的'Tag'再入侵攻擊時,你應該意識到你的代碼有問題。至於解決方案是什麼,很難說。我想你只需要將代碼從'OnBeforeOpen'中移出並放在調用Open'之前。擺脫那個可怕的'Tag'黑客。當事件處理程序將實例傳遞給您時,請使用它而不是使用該類中的字段。 –

+0

您創建數據集並將其打開。打開它並在打開之前創建它。 – Victoria

+0

我已經覆蓋了DoBeforeOpen過程我使用在該部分中的BeforeOpen中編寫的代碼。我不希望程序員在外部調用CreateDataSet,所以我想把它放在我的組件DoBeforeOpen方法中。 –

回答

3

不要定義和創建在BeforeOpen活動數據集。要創建(並打開),在內存中的數據集做:

procedure TForm1.ButtonCreateClick(Sender: TObject); 
begin 
    ClientDataSet1.Close; 
    ClientDataSet1.FieldDefs.Clear; 
    ClientDataSet1.FieldDefs.Add('ID', ftInteger); 
    ClientDataSet1.FieldDefs.Add('Collection', ftString, 50); 
    ClientDataSet1.CreateDataSet; 
end; 

要添加記錄做:

procedure TForm1.ButtonAppendClick(Sender: TObject); 
begin 
    ClientDataSet1.Append; 
    ClientDataSet1.FieldByName('ID').AsInteger := 1; 
    ClientDataSet1.FieldByName('Collection').AsString := 'My collection'; 
    ClientDataSet1.Post; 
end; 

編輯當前記錄做的事:

procedure TForm1.ButtonUpdateClick(Sender: TObject); 
begin 
    ClientDataSet1.Edit; 
    ClientDataSet1.FieldByName('Collection').AsString := 'My collection upd.'; 
    ClientDataSet1.Post; 
end;