Einzelnen Beitrag anzeigen

TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.070 Beiträge
 
Delphi 10.4 Sydney
 
#1

Memory Error Detected: TArray<System.WideString> im Record im Record im Byte-Array

  Alt 12. Aug 2019, 15:54
Hallo Gemeinde,

Vorgeschichte:
Ich bin gerade dabei im jahre alten Quelltexten Sachen zu optimieren. Delphi-Version ist XE5.
Ein Kernstück des vorhandenen Frameworks basiert darauf, sich komplexe Result-Werte als records quer durchs Programm und auch über Modulgrenzen hinweg zu schicken.

Optimierungsbedarf:
Für String-Informationen wurden dafür fixe/statische Char-Arrays verwendet, stellenweise 1000 bis 2000 Zeichen groß, obwohl in 99 % der Fälle viel weniger reichen würden.

Lösungsidee:
Meine Idee ist, diese statischen Char-Arrays mit dem System.WideString (BSTR) zu ersetzen.
So kann das auch über Modulgrenzen hinweg genutzt werden, die Speicherverwaltung der Strings macht Windows und man hat viel weniger eigentlichen Speicherbedarf, da die Strings halt nur so lang sind, wie man reinsteckt.

Problem:
Es gibt ein generisches Container-Record, in das per eigenen Implicit-Operator die - ich sag mal Sub-Records - transformiert werden können.
Dieses Container-Record speichert die Sub-Records als Byte-Array.
Dabei hat mir FastMM ein Problem aufgezeigt.
Ich habe das Problem anhand eines minimalen Beispiels mit Kommentaren skizziert.
Anschließend befindet sich die Meldung vom externen FastMM (ja, den mit extra DLL).
Ich weiß, dass mein Problem die Sache mit den Record im Record ist, weil so die Referenzzählung vom WideString-Array beim Move kaputt geht.
Aber ich bin gerade so vernagelt und finde keine Lösung.
Hat jemand eine Idee, wie ich die Idee in diesem Rahmen lösen kann?

Delphi-Quellcode:
program MinimalExample;

{$APPTYPE CONSOLE}

{$R *.res}


uses
    FastMM4,
    System.SysUtils;

type
    TContainer = record
        ExternalData: TBytes;
    end;

    TElementA = record
        MyStrings: TArray<Widestring>;
        MyNumber: UInt64;
    end;

    TElementB = record
    public
      type
        TElementBData = record
            A: TElementA;
        end;
    public
        Data: TElementBData;
    end;

procedure Main;
var
    A: TElementA;
    B, B2: TElementB;
    Container: TContainer;
    I: Integer;
    CopyLength: Integer;
begin
    // TElementA zum Leben erwecken und füllen
    A := System.Default(TElementA);
    SetLength(A.MyStrings, 5);
    for I := System.Low(A.MyStrings) to System.High(A.MyStrings) do
    begin
        A.MyStrings[I] := Format('%d%d%d', [I, I, I]);
    end;

    // TElementA dem TElementB.Data unterschieben. ASM: call @CopyRecord.
    // Im echten Quelltext ein Implicit-Operator an TElementB dran.
    B := System.Default(TElementB);
    B.Data.A := A;

    // Container-Record, was die Daten von TElementB in einen Byte-Array halten soll.
    // Im echten Quelltext eine externe Funktion an TContainer, der ich sozusagen nur Pointer und Größe auf B.Data gebe.
    Container := System.Default(TContainer);
    CopyLength := SizeOf(B.Data);
    SetLength(Container.ExternalData, CopyLength);
    // ---> Das Move zerstört wahrscheinlich den RefCount vom B.Data.A.Strings-Array
    Move((@B.Data)^, Container.ExternalData[0], CopyLength);

    // Zurückumwandeln von Container-Element
    // Im echten Quelltext auch wieder ein Implicit-Operator an TElementB dran.
    B2 := System.Default(TElementB);
    // ---> Das Move kopiert zwar den Pointer vom Array, so das B2.Data.A.Strings gefüllt ist
    // ---> aber hinterher FastMM zurecht sagt, dass man dafür in die Delphi-Hölle kommt.
    CopyLength := Length(Container.ExternalData);
    Move(Container.ExternalData[0], B2.Data, CopyLength);

    for I := System.Low(B2.Data.A.MyStrings) to System.High(B2.Data.A.MyStrings) do
    begin
        Writeln(B2.Data.A.MyStrings[I]);
    end;
end;

begin
    Main;
end.
Code:
---------------------------
MinimalExample.exe: Memory Error Detected
---------------------------
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): 0(1)

The previous block size was: 28

This block was previously allocated by thread 0x36E0, and the stack trace (return addresses) at the time was:
4041F9 [System.pas][System][@ReallocMem$qqrrpvi][4508]
407A0D [System.pas][System][DynArraySetLength$qqrrpvpvipi][33677]
407B3E [System.pas][System][@DynArraySetLength$qqrv][33756]
41DEED [MinimalExample.dpr][MinimalExample][Main$qqrv][42]
406498 [System.pas][System][InitUnits$qqrv][21918]
406504 [System.pas][System][@StartExe$qqrp23System.PackageInfoTablep17System.TLibModule][22052]
4203F5 
771A6359 [BaseThreadInitThunk]
77B67A94 [RtlGetAppContainerNamedObjectPath]
77B67A64 [RtlGetAppContainerNamedObjectPath]


The allocation number was: 113


The block was previously freed by thread 0x36E0, and the stack trace (return addresses) at the time was:
407B7A [System.pas][System][@DynArrayClear$qqrrpvpv][33939]
4075DE [System.pas][System][@FinalizeArray$qqrpvt1ui][31219]
4074BD [System.pas][System][@FinalizeRecord$qqrpvt1][30891]
4075BD [System.pas][System][@FinalizeArray$qqrpvt1ui][31178]
4074BD [System.pas][System][@FinalizeRecord$qqrpvt1][30891]
4075BD [System.pas][System][@FinalizeArray$qqrpvt1ui][31178]
4074BD [System.pas][System][@FinalizeRecord$qqrpvt1][30891]
4075BD [System.pas][System][@FinalizeArray$qqrpvt1ui][31178]
41E04F [MinimalExample][Main$qqrv]
4203F5 
771A6359 [BaseThreadInitThunk]


The current thread ID is 0x36E0, and the stack trace (return addresses) leading to this error is:
40D780 [FastMM4.pas][FastMM4][CheckBlocksOnShutdown$qqro][11424]
40E7C4 [FastMM4.pas][FastMM4][FinalizeMemoryManager$qqrv][12966]
40E830 [FastMM4.pas][FastMM4][Finalization$qqrv][13065]
40642C [System.pas][System][FinalizeUnits$qqrv][21786]
406826 [System.pas][System][@Halt0$qqrv][23185]
4203FA
771A6359 [BaseThreadInitThunk]
77B67A94 [RtlGetAppContainerNamedObjectPath]
77B67A64 [RtlGetAppContainerNamedObjectPath]



Current memory dump of 256 bytes starting at pointer address 7F93B690:
87 92 42 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 2C A7 7C 9E
80 80 80 80 80 80 80 80 00 00 00 00 11 B6 93 7F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7C 00 00 00 F9 41 40 00 0D 7A 40 00 3E 7B 40 00 AB DF 41 00 F5 03 42 00 59 63 1A 77 94 7A B6 77
64 7A B6 77 00 00 00 00 00 00 00 00 00 00 00 00 E0 36 00 00 E0 36 00 00 A6 41 40 00 7A 7B 40 00
DE 75 40 00 BD 74 40 00 6B E0 41 00 F5 03 42 00 59 63 1A 77 94 7A B6 77 64 7A B6 77 00 00 00 00
00 00 00 00 18 00 00 00 00 00 00 00 B5 7C 6D 4F 88 92 42 00 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 4A 83 92 B0 80 80 80 80 80 80 80 80 80 80 80 80 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
‡  ’  B . €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  , §  |  ž
€  €  €  €  €  €  €  €  . . . . . ¶  “    . . . . . . . . . . . . . . . .
|  . . . ù  A @  . . z @  . > {  @  . «  ß  A . õ  . B . Y c . w ”  z ¶  w
d z ¶  w . . . . . . . . . . . . à  6  . . à  6  . . ¦  A @  . z {  @  .
Þ  u @  . ½  t @  . k à  A . õ  . B . Y c . w ”  z ¶  w d z ¶  w . . . .
. . . . . . . . . . . . µ  |  m O ˆ  ’  B . €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  J ƒ  ’  °  €  €  €  €  €  €  €  €  €  €  €  €  . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

---------------------------
OK  
---------------------------
  Mit Zitat antworten Zitat