Einzelnen Beitrag anzeigen

peterbelow

Registriert seit: 12. Jan 2019
Ort: Hessen
672 Beiträge
 
Delphi 11 Alexandria
 
#3

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

  Alt 12. Aug 2019, 15:48
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?
Du hast da einen Denkfehler drin. Deine alten array [0..x] of char felder sind value-typen, können also ohne Probleme von speicher zu speicher kopiert werden. Ein TArray<Widestring> ist aber ein referenz-Typ, genau wie Widestring selbst.


Delphi-Quellcode:
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;
   --snip--
    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);
Und da knalls schon, sizeof(B.Data) == sizeof(TElementA) == sizeof(pointer)+sizeof(uint64).

D. h. die Widestrings in dem MYStrings array sind selbst garnicht mitgezählt. Was Du dann kopierst ist die Addresse des Arrays, nicht sein Inhalt. Und den kannst Du auch garnicht blind kopierene, da der Array ja auch nur Referenzen enthält, nicht direkt die zu kopierenden Zeichen.

Du brauchst für jeden Record-Typ Methoden, die den Inhalt in einen Byte-Array kopieren (streamen) und daraus wieder restaurieren können. Das geht nicht wirklich generisch, obwohl man da unter Verwendung von RTTI ziemlich weit kommen kann. Ich weis nicht, wie gut das Marshalling für JSON Support bei XE5 schon war, aber das macht was ähnliches.
Peter Below
  Mit Zitat antworten Zitat