Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   FastMM und aktuelles Delphi (https://www.delphipraxis.net/189654-fastmm-und-aktuelles-delphi.html)

Der schöne Günther 5. Jul 2016 17:03

FastMM und aktuelles Delphi
 
Delphi 10.1 Berlin.
Datei -> Neu -> Geräteübergreifende Anwendung.

Ich trage in die .DPR-Datei als erste Unit noch FastMM4 ein:
Delphi-Quellcode:
program Project1;

uses
  FastMM4,
  System.StartUpCopy,
  FMX.Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
Sobald ich das leere Formular schließe bekomme ich eine AV mit folgendem Stack

Code:
System.TMonitor.Destroy
System.TInstBucket.Finalize
System.TInstHashMap.Finalize
System.Finalization
System.FinalizeUnits
System._Halt0
:00409DC5 System::__linkproc__ Halt0()
Nach dem Zufallsprinzip auch mal an anderen Stellen. Lasst ich FastMM weg, ist im Debugger nichts zu bemerken. In einer VCL-Anwendung ist auch nichts zu bemerken.

Ich habe FastMM noch von Sourceforge geladen:
https://sourceforge.net/projects/fastmm/

Es wäre aber langweilig wenn sich ein Projekt nicht balkanisiert, zu finden auch auf Github:
https://github.com/pleriche/FastMM4 (inkl. 13 Forks)


Kann mir jemand sagen ob ich etwas falsch mache oder funktioniert FastMM auf neueren Versionen nicht mehr?


PS: Ich sehe gerade, ich bin nicht der einzige dem das aufgefallen ist:
https://github.com/pleriche/FastMM4/issues/18

Ich hätte gedacht dass bei so etwas die Welt früher im Fünfeck springt.

Stevie 5. Jul 2016 17:14

AW: FastMM und aktuelles Delphi
 
https://github.com/pleriche/FastMM4/issues/18

Die Ursache des Problems ist ganz einfach. Die Hashmap für die weak references in System wird lazy initialized (das erste mal, wenn eine weak reference genutzt wird).
Das passiert irgendwo beim Form erstellen in den Tiefen von FMX (genauer gesagt dort, wo [Weak] an einem Interface Feld steht, so wie in diesem Fall beim TContentInflater<T>).

D.h. der Speicher wird hier vom schon initialisierten FastMM verwaltet.

Das Abräumen der Hashmap passiert aber beim finalization von System und das passiert nach dem finalization der FastMM unit.

Ich hab mal testhalber das
Delphi-Quellcode:
FinalizeMemoryManager
dort auskommentiert und es gibt zumindest keine AV mehr, da auch der Speicher der Weakref Hashmap von FastMM aufgeräumt wird.


Edit: Die Option NeverUninstall (einfach bei den Conditional defines im Projekt hinzufügen) sollte reichen.

Der schöne Günther 5. Jul 2016 19:29

AW: FastMM und aktuelles Delphi
 
Vielen Dank für die ausführliche Erklärung :thumb:

Genau das war mein Verdacht, hatte das aber ausgeschlossen da ich fälschlicherweise im Kopf hatte man würde eine spezielle Exception und keine einfache AV bekommen wenn das der Fall ist.

Der schöne Günther 6. Jul 2016 09:01

AW: FastMM und aktuelles Delphi
 
Ok, jetzt muss ich doch noch einmal nachbohren.

Auf 10 Seattle (+Update 1) habe ich ein ähnliches Problem. Ich bekomme reproduzierbar beim Beenden der Anwendung eine AV mit folgendem Stack:
Code:
FastMM4.DebugGetMem(???)
System._GetMem(???)
System._NewUnicodeString(???)
:00406DF6 System::__linkproc__ GetMem(Size=????)
System.SysUtils.Format(???,???,$96360C)
System.SysUtils.Format(???,???)
FMX.Presentation.Factory.TPresentationProxyFactory.GeneratePresentationName(TEdit,Platform)
FMX.Presentation.Factory.TPresentationProxyFactory.Unregister(TEdit,Platform,TWinPresentationProxy<FMX.Edit.Win.TWinNativeEdit>)
FMX.Edit.Win.Finalization
System.FinalizeUnits
System._Halt0
:00409A75 System::__linkproc__ Halt0()
NeverUninstall, LeakReporting, CheckHeapForCorruption - Das alles half nichts.


Es ist der Punkt Memory Manager Sharing -> EnableBackwardCompatibleMMSharing. Wenn ich das aktiviere, dann geht es! Die Beschreibung ist
Zitat:

Define this to enable backward compatibility for the memory manager sharing mechanism used by Delphi 2006 and 2007, as well as older FastMM versions.
Wie kommt hier die "older FastMM versions" ins Spiel? Ich dachte, ich bin so modern, ich brauche das nicht. Da lag ich wohl wieder falsch. Kann mir jetzt noch jemand helfen zu verstehen, was hier abläuft und warum ich diesen Eintrag brauche?


Kommando zurück, es bringt doch nichts.

Zum Nachstellen reicht allerdings kein leeres Formular mehr aus, man muss auch einen TTreeView darauf legen und die Größe etwas größer machen. Komische Welt.

TiGü 6. Jul 2016 10:12

AW: FastMM und aktuelles Delphi
 
Das Problem hatte ich in unserer neuen VCL-Anwendung auch schon ein paar Mal bemerkt.
Wir verwenden das Delphi eigene Tethering und zusammen mit den FastMM4 hat es an der von Günther beschriebenen Stelle geknallt.

Delphi-Quellcode:
procedure TInstBucket.Finalize;
var
  I: Integer;
begin
  for I := 0 to FCount - 1 do
    FInstItems[I].Free;
  FCount := 0;
  FLock.Destroy; // <--- da knallt's dann!
  SetLength(FInstItems, 0);
end;
Dachte immer wir machen irgendwas falsch und/oder der Entwickler bei Emba hat da fälschlicherweise Destroy anstatt Free aufgerufen.

Stevie 6. Jul 2016 14:00

AW: FastMM und aktuelles Delphi
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1342065)
Zum Nachstellen reicht allerdings kein leeres Formular mehr aus, man muss auch einen TTreeView darauf legen und die Größe etwas größer machen. Komische Welt.

Kann ich nicht nachstellen.

Der schöne Günther 6. Jul 2016 17:19

AW: FastMM und aktuelles Delphi
 
Liste der Anhänge anzeigen (Anzahl: 1)
Danke für's Ausprobieren. Ich lüge aber nicht.

Embarcadero® RAD Studio 10 Seattle Version 23.0.21418.4207

Mit XE7 kein Problem. Mit 10.1 Berlin kein Problem. Nur 10 Seattle.

Ich habe es mal als Testprojekt angehängt, habe ich alles richtig gemacht?

Stevie 7. Jul 2016 10:44

AW: FastMM und aktuelles Delphi
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1342098)
Ich lüge aber nicht.

Hab ich auch nicht behauptet - kann es aber immer noch nicht nachstellen - habe Seattle mit Subscription Update 1.

Der schöne Günther 7. Jul 2016 11:19

AW: FastMM und aktuelles Delphi
 
Komisch, dann ist mein PC wohl kaputt. Ist auch halb so wild, XE7 und 10.1 gehen ja. Vielen Dank fur's Mitfiebern ;-)

Headbucket 12. Apr 2017 08:29

AW: FastMM und aktuelles Delphi
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo zusammen,

ich habe zZ ein ähnliches Problem mit der Verwendung von FastMM in Verbindung mit Frames, welche ein Interface implementieren:
Delphi-Quellcode:
type
  TMyFrame = class(TFrame, IMyInterface)
    lbl1: TLabel;
  private
    { Private-Deklarationen }
  public
    procedure MyInterfaceMethode;
  end;
Ein kleines Testprojekt habe ich angehängt. Beim Beenden der Anwendung bekomme ich eine access violation mit folgendem Stack:
Code:
System._IntfClear(???)
:0040d998 @IntfClear + $10
System.TObject.Free
System.Classes.TComponent.DestroyComponents
Vcl.Forms.DoneApplication
System.SysUtils.DoExitProc
System._Halt0
Ohne FastMM geht alles und auch sonst verwende ich FastMM in sämtlichen Projekten. Aber mit Frames und Interfaces habe ich Probleme... .
Liegt der Fehler bei mir? Falls nicht: Wie kann ich die access violation abstellen? Die Hinweise hier im Thread halfen leider nicht.

Grüße
Headbucket

Bernhard Geyer 12. Apr 2017 08:50

AW: FastMM und aktuelles Delphi
 
Zitat:

Zitat von Headbucket (Beitrag 1367273)
Hallo zusammen,

ich habe zZ ein ähnliches Problem mit der Verwendung von FastMM in Verbindung mit Frames, welche ein Interface implementieren:

Ich vermute du musst die Refernzzählung deaktivieren.
Ansonsten hast du u.U. doppelte Freigabe. Einerseits über den Parent/owner und andererseits über das Interface.

Headbucket 12. Apr 2017 10:49

AW: FastMM und aktuelles Delphi
 
Das war auch mein erster Gedanke.

Mein Frame ist ja aber von TComponent abgeleitet. Wird dort nicht so oder so die Referenzzählung deaktiviert?
Ansonsten wäre ich über Hinweise dankbar, wie man diese abschaltet :stupid:

Und was hat FastMM damit zu tun? Wenn das ganze doppelt freigegeben wird, dann sollte doch IMMER ein Fehler kommen und nicht nur mit FastMM.

Danke aber schonmal für die Antwort!

Grüße

Der schöne Günther 12. Apr 2017 10:58

AW: FastMM und aktuelles Delphi
 
Der Ablauf ist doch folgender
- Erstelle Frame
- Habe interface-basierte Referenz auf das Frame-Objekt in einem Objekt "X"
- Zerstöre Frame
- Habe weiterhin interface-basierte Referenz auf das Frame-Objekt
- Zerstöre Objekt "X"
- Interface-basierte Referenz wird genullt, versucht Referenzzähler auf den mittlerweile zerstörten Frame zu verringern (also die Methode _Release() aufrufen)


Ohne FastMM kommt hier zur Laufzeit, denke ich, aus einem von zwei Gründen keine Exception
  1. Wenn die Anwendung abgeräumt wird ist der Standard-Speichermanager relativ großzügig was das Herunterschlucken von AVs angeht. Zumindest kommt es mir so vor
  2. Wenn er _Release() auf dem zerstörten Frame aufrufen will bemerkt er nichts ungewöhnliches. Alles klappt wie es soll. Kein Grund sich zu beschweren. Bei FastMM hingegen gibt es eine Einstellung dass beim Zerstören eines Objekts sein gesamter Speicher mit irgendeinem Muster komplett überschrieben wird. Somit ist kein "es könnte noch klappen" mehr möglich, sondern das Aufrufen einer Methode auf einem toten Objekt geht garantiert schief. Das ist bewusst gemacht um solche Fehler zu finden.


Ab Delphi 10.1 Berlin könnte man das Problem ganz gut mit dem [weak]-Attribut angehen, oder? Ich habe mich damit immer noch nicht befasst :oops:

SebastianZ 12. Apr 2017 11:07

AW: FastMM und aktuelles Delphi
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1367308)
Der Ablauf ist doch folgender
- Erstelle Frame
- Habe interface-basierte Referenz auf das Frame-Objekt in einem Objekt "X"
- Zerstöre Frame
- Habe weiterhin interface-basierte Referenz auf das Frame-Objekt
- Zerstöre Objekt "X"
- Interface-basierte Referenz wird genullt, versucht Referenzzähler auf den mittlerweile zerstörten Frame zu verringern (also die Methode _Release() aufrufen)

Aus meiner Sicht liegt hier des Pudels Kern. Ich habe dein Forms.Main wie folgt geändert:

Delphi-Quellcode:
unit Forms.Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  uInterface,
  Frames.MyFrame, Vcl.ExtCtrls;

type
  TMainForm = class(TForm)
    pnl1: TPanel;
  private
    FMyFrame: IMyInterface;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    { Public-Deklarationen }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

constructor TMainForm.Create(AOwner: TComponent);
begin
  inherited;

  FMyFrame := TMyFrame.Create(Self);
  TMyFrame(FMyFrame).Parent := pnl1;
  TMyFrame(FMyFrame).Align := alClient;
end;

destructor TMainForm.Destroy;
begin
  FMyFrame := nil;

  inherited;
end;

end.
Dadurch sollte alles korrekt abgebaut werden.

Bei deiner Variante bleibt die Referenz im Interface stehen, er versucht das dazugehörige Objekt abzubauen, das es nicht mehr gibt, und die Exception kommt.

Headbucket 12. Apr 2017 11:25

AW: FastMM und aktuelles Delphi
 
Vielen Dank für die Antworten!

Klingt logisch und das manuelle nil-Setzen löst unabhängig von dem im Create zugewiesenen Owner das Problem.

Wird mein Frame dann jetzt nur noch vom Interface freigegeben (in dem Moment, wo ich nil setze), da der Owner keinen Bezug mehr zum Frame hat?

Grüße
Headbucket

Der schöne Günther 12. Apr 2017 11:34

AW: FastMM und aktuelles Delphi
 
Nein, ein TFrame leitet sich ab von TComponent. Und wenn du schaust wie in TComponent die MEthoden _AddRef und _Release implementiert sind: Da passiert nichts. Eine TComponent ist immer so vorgesehen dass man sich manuell um ihre Lebenszeit kümmert. Entweder über den Owner. Oder wenn kein Owner da ist, dann über den schlauen Programmierer der weiß wann die Zeit gekommen ist 8-)

Siehe auch: http://stackoverflow.com/q/2182612/2298252

Headbucket 12. Apr 2017 11:53

AW: FastMM und aktuelles Delphi
 
:thumb:

Besten Dank!

himitsu 12. Apr 2017 14:03

AW: FastMM und aktuelles Delphi
 
Du hast dienen Frame als Interface gespeichert, das sollte man niemals machen.

TComponent hat ein IInterface ohne Referenzzählung.


Die VCL ist leider etras "böswillig", denn die Freigabe wird nicht "nur" über Free und den Owner geregelt, sondern auch der Parent gibt alle Komponenten frei, welche auf ihm liegen, wenn man ihn löscht.

Da die VCL aber die KlassenFelder auf nil setzt, wenn der owner ein Published-Feld mit dem selben Namen besitzt, fällt es meistens nicht auf,
da bei Freigabe eines Objektes auch die Felder genilt werden.
(etwas unglücklich ist, dass die VCL nicht auf den Typ achtet, hart auf TComponent castet und alles überschreibt, was zufällig so heißt)


Deine Interfacevariable ist aber keine [Weak]-Referenz und bleibt somit gesetzt.



PS: FastMM oder nicht FastMM ... seit 2006 ist ein FastMM immer im Delphi drin.


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:14 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz