TXMLDocument.Active := False causes FastMM4 errormessage “FastMM detected that a block has been modified after being freed”

半城伤御伤魂 提交于 2019-12-12 19:21:27

问题


we have a strange effect using Delphi XE8 with FastMM4 (Version 4.992) in FullDebugMode.

To reproduce the effect, just create a new TForm application, put FastMM4 in the first line of the DPR file, put a Button on the Form and put the following code in the clickhandler:

(You need to have FastMM 4 installed, FullDebugMode must be enabled in the FastMM4Options.inc file and the FullDebugMode.dll has to be in the output folder of your programm!)

procedure TForm3.Button4Click(Sender: TObject);
var
    dm: tdatamodule;
    doc: txmldocument;
begin
    //this causes the messagebox to be shown directly after clicking the button. 
    //without it the box is shown when the program is exited.
    FastMM4.FullDebugModeScanMemoryPoolBeforeEveryOperation := true;        

    //prepare a xml document
    dm := tdatamodule.create(nil);
    doc := txmldocument.create(dm);
    doc.LoadFromXml('<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?><root/>');

    //doc.Active := true;   //no need to set active to true. doc is already active at this point!

    //the following does matter. ChildNodes needs to be accessed twice!
    //doc.DocumentElement.ChildNodes.FindNode('first');
    //doc.DocumentElement.ChildNodes.FindNode('second');

    //alternative access:
    doc.DocumentElement.ChildNodes.count;                                       //first access to ChildNodes
    doc.DocumentElement.ChildNodes.count;                                       //second access to ChildNodes

    //this does matter. if doc stays active then there is no problem!
    doc.Active := false;                                                        //active needs to be set to false!

    //doc.free; //doesn't matter if doc is freed manually. doc will be freed when datamodule is freed. problem occurs either way.
    dm.free;
end;

When you click the button, FastMM4 will show a large messagebox with the following report:

FastMM has detected an error during a free block scan operation. FastMM detected that a block has been modified after being freed. 

Modified byte offsets (and lengths): 80(1)

The previous block size was: 228

This block was previously allocated by thread 0xC74, and the stack trace (return addresses) at the time was:
406C46 [System.pas][System][@GetMem$qqri][4565]
407A73 [System.pas][System][TObject.NewInstance$qqrv][15975]
5DF25D [Xml.XMLDoc.pas][Xml.XMLDoc][Xmldoc.TXMLDocument.NewInstance$qqrv][2368]
408202 [System.pas][System][@ClassCreate$qqrpvzc][17290]
5DF116 [Xml.XMLDoc.pas][Xml.XMLDoc][Xmldoc.TXMLDocument.$bctr$qqrp25System.Classes.TComponent][2344]
5E2094 [Unit3.pas][Unit3][TForm3.Button4Click$qqrp14System.TObject][47]
407E6F [System.pas][System][@IsClass$qqrxp14System.TObjectp17System.TMetaClass][16465]
51EB39 [Vcl.Controls.pas][Vcl.Controls][Controls.TControl.Click$qqrv][7361]
535CF3 [Vcl.StdCtrls.pas][Vcl.StdCtrls][Stdctrls.TCustomButton.Click$qqrv][5327]
536801 [Vcl.StdCtrls.pas][Vcl.StdCtrls][Stdctrls.TCustomButton.CNCommand$qqrr26Winapi.Messages.TWMCommand][5788]
51E5C8 [Vcl.Controls.pas][Vcl.Controls][Controls.TControl.WndProc$qqrr24Winapi.Messages.TMessage][7245]

The block was previously used for an object of class: TXMLDocument

The allocation number was: 650

The block was previously freed by thread 0xC74, and the stack trace (return addresses) at the time was:
406C62 [System.pas][System][@FreeMem$qqrpv][4613]
407A91 [System.pas][System][TObject.FreeInstance$qqrv][15984]
40824D [System.pas][System][@ClassDestroy$qqrxp14System.TObject][17333]
5DF241 [Xml.XMLDoc.pas][Xml.XMLDoc][Xmldoc.TXMLDocument.$bdtr$qqrv][2363]
4C3661 [System.Classes.pas][System.Classes][Classes.TComponent.DestroyComponents$qqrv][15648]
4C3100 [System.Classes.pas][System.Classes][Classes.TComponent.$bdtr$qqrv][15445]
4C5543 [System.Classes.pas][System.Classes][Classes.TDataModule.$bdtr$qqrv][16694]
407B97 [System.pas][System][TObject.Free$qqrv][16052]
5E20F2 [Unit3.pas][Unit3][TForm3.Button4Click$qqrp14System.TObject][63]
51EB39 [Vcl.Controls.pas][Vcl.Controls][Controls.TControl.Click$qqrv][7361]
535CF3 [Vcl.StdCtrls.pas][Vcl.StdCtrls][Stdctrls.TCustomButton.Click$qqrv][5327]

The current thread ID is 0xC74, and the stack trace (return addresses) leading to this error is:
416526 [fastmm4.pas][FastMM4][InternalScanMemoryPool$qqruiui][10018]
416601 [fastmm4.pas][FastMM4][ScanMemoryPoolForCorruptions$qqrv][10092]
4161DF [fastmm4.pas][FastMM4][DebugFreeMem$qqrpv][9761]
406C62 [System.pas][System][@FreeMem$qqrpv][4613]
407A91 [System.pas][System][TObject.FreeInstance$qqrv][15984]
40824D [System.pas][System][@ClassDestroy$qqrxp14System.TObject][17333]
407B8A [System.pas][System][TObject.$bdtr$qqrv][16044]
40D5DD [System.pas][System][TInterfacedObject._Release$qqsv][37311]
40D4B7 [System.pas][System][@IntfClear$qqrr44System.%DelphiInterface$17System.IInterface%][36327]
40B189 [System.pas][System][@FinalizeArray$qqrpvt1ui][31704]
40B079 [System.pas][System][@FinalizeRecord$qqrpvt1][31407]

Current memory dump of 256 bytes starting at pointer address 7EA18B60:
98 72 5F 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 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 7F 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 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 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 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 BD 4A 52 7A 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 00 00 00 00 00 00 00 00
˜  r  _  .  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €    €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  ½  J  R  z  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  .  .  .  .  .  .  .  .

If we interpret this correctly, then FastMM is telling us, that the Memory of the TXMLDocument Object has been modified, after it was freed.

"Some Code" seems to have written the $7F in the middle of all these $80 in the memoryblock of the TXMLDocument that has already been freed.

This does happen only if ChildNodes is accessed twice (!) and if the Active property of the TXMLDocument is set to false before the Object is freed!

Questions:

Can someone explain what is going on here?

  • is setting TXMLDocument.Active to false generally considered wrong or "bad"? (Is it known to cause Problems?)

  • are we making some other mistake here?

  • is this a problem in FastMM4?

  • is this a problem in TXMLDocument?

Additional observation:

If the TXMLDocument is freed without active being set to false, then there is no problem.

If we look at the destructor of TXMLDocument we see that there is some additional code before active is set to false there:

destructor TXMLDocument.Destroy;
begin
  Destroying;
  if FOwnerIsComponent and Active and Assigned(FDocumentNode) and (FRefCount > 1) then      //additional code
    (FDocumentNode as IXMLNodeAccess).ClearDocumentRef;                                     //additional code
  SetActive(False);
  FreeAndNil(FXMLStrings);
  inherited;
end;

Now, if we modify our own examplecode and call

(FDocumentNode as IXMLNodeAccess).ClearDocumentRef; 

before setting active to false, then the problem is gone!

Code would look something like this:

type
  TMyXMLDocument = class(TXMLDocument);

procedure TForm3.Button4Click(Sender: TObject);
var
    dm: tdatamodule;
    doc: txmldocument;
begin
    FastMM4.FullDebugModeScanMemoryPoolBeforeEveryOperation := true;

    dm := tdatamodule.create(nil);
    doc := txmldocument.create(dm);
    doc.LoadFromXml('<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?><root/>');

    doc.DocumentElement.ChildNodes.count;
    doc.DocumentElement.ChildNodes.count;

    (TMyXMLDocument(doc).GetDocumentElement as IXMLNodeAccess).ClearDocumentRef;  //<-- with this  additional hack the problem is gone!

    doc.Active := false;                                                        //no more problem!

    dm.free;
end;

回答1:


When calling methods/functions that return interfaces, the compiler implicitly generates variables to put these results into. They live until the end of the method and are then being finalized/cleared.

In your case doc.DocumentElement.ChildNodes does 2 method calls that return interfaces. Now when you destroy the TXMLDocument instance these implicit variables still point to some memory and due to the _Release call being made when the compiler generated code calls IntfClear they call into some method that has no object anymore - FastMM is able to track and report that.

So this mentioned call is translated into this:

var
  ...
  nodes1: IXMLNodeList;
  node1: IXMLNode;
  nodes2: IXMLNodeList;
  node2: IXMLNode;
begin
  ...

  node1 := doc.DocumentElement;
  nodes1 := node1.ChildNodes;
  nodes1.count;
  node2 := doc.DocumentElement;
  nodes2 := node2.ChildNodes;
  nodes2.count;

  dm.free;

  nodes1 := nil;
  node1 := nil;
  nodes2 := nil;
  node2 := nil; // <- boom

In many cases this error does not manifest without using FastMM unless the previously deallocated memory was not being reused for another allocation which then results in weird AVs.

Rule of thumb: do not destroy instances that are referenced by some interface in the same scope as this might lead to implicitly created interface variables still pointing to them.



来源:https://stackoverflow.com/questions/51422248/txmldocument-active-false-causes-fastmm4-errormessage-fastmm-detected-that-a

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!