Autor |
Beitrag |
Christian213
      
Beiträge: 66
Erhaltene Danke: 3
Win XP, Win 7 64Bit
Lazarus 1.0.10
|
Verfasst: Mi 16.10.13 13:06
Hallo,
meine Anwendung schmeißt beim Aufruf von Application.Terminate einen Access Violation Error.
Ich bin schon soweit gekommen, dass der Fehler irgendwie mit zur Laufzeit erzeugten Formularen zusammenhängen muss (bzw. das automatische Abräumen deren beim Beenden).
Gibt es eine Best-Practise wie man solche Fehler am sinnvollsten einkreist?
Bin für jeden Tipp dankbar.
Danke und viele Grüße
Christian
|
|
baumina
      
Beiträge: 305
Erhaltene Danke: 61
Win 7
Delphi 10.2 Tokyo Enterprise
|
Verfasst: Mi 16.10.13 13:21
Application.Terminate wird im Normalfall nicht benutzt. Gibt es einen bestimmten Grund weswegen Du ein Application.Terminate aufrufst? Kannst du stattdessen kein Hauptformular.Close aufrufen?
Für diesen Beitrag haben gedankt: Christian213
|
|
hathor
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 16.10.13 13:36
Close schliesst diesen Befehl (Application.Terminate ) ein:
Zitat:
Rufen Sie Terminate auf, um die Anwendung programmgesteuert zu beenden. Durch einen Aufruf der Methode Terminate wird das Anwendungsobjekt nicht einfach gelöscht, die Anwendung kann vielmehr ordnungsgemäß heruntergefahren werden.
Die Methode Terminate ruft die Windows-API-Funktion PostQuitMessage auf, sodass die Anwendung ordnungsgemäß heruntergefahren wird. Sie brauchen Terminate nicht direkt aufzurufen.
Diese Methode wird automatisch für eine WM_QUIT-Botschaft und beim Schließen des Hauptformulars aufgerufen.
Für diesen Beitrag haben gedankt: Christian213
|
|
Nersgatt
      
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Mi 16.10.13 13:41
Christian213 hat folgendes geschrieben : | Ich bin schon soweit gekommen, dass der Fehler irgendwie mit zur Laufzeit erzeugten Formularen zusammenhängen muss (bzw. das automatische Abräumen deren beim Beenden).
Gibt es eine Best-Practise wie man solche Fehler am sinnvollsten einkreist?
|
Vielleicht greifst Du in irgend einem Destructor auf eine Objektinstanz zu, die schon vorher irgendwo abgeräumt wurde.
Best Practise um das zu vermeiden ist meiner Meinung nach, im Vorfeld sowas zu vermeiden. Sprich, ich erstelle meine Instanzen von Formularen selbst, wo ich ich sie brauche, und sobald ich sie nicht mehr brauche gebe ich sie frei. Außerdem verwende ich Instanzvariablen in der Regel nicht doppelt. Sprich, für eine Instanzvariable gibt es genau eine Stelle im Code, wo ich die Instanz erzeuge und genau eine Stelle, wo ich die Instanz wieder frei gebe. So behält man den Überblick und kann solche Probleme im Vorfeld vermeiden.
Eingrenzen ist natürlich schwierig. Ich würde anfangen und die Verdächtigen nicht mit .free freigeben, sondern mit FreeAndNil(). So kannst Du vermeiden, dass der Fehler nur sporadisch auftritt, sondern er tritt sicher auf. Und dann kann man das auch besser einkreisen.
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
Für diesen Beitrag haben gedankt: Christian213
|
|
Christian213 
      
Beiträge: 66
Erhaltene Danke: 3
Win XP, Win 7 64Bit
Lazarus 1.0.10
|
Verfasst: Mi 16.10.13 13:54
Hallo zusammen,
Danke erstmal für die Tipps! Ich werde die Punkte mal durchgehen und melde mich dann wieder.
VG
Christian
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 16.10.13 13:58
Ich würde einfach einmal FastMM einbinden, das findet schon viele solcher Probleme, sprich sagt wo das Problem herkommt, wenn es auftritt.
Dann wäre da noch madExcept, das für den privaten Gebrauch ebenfalls kostenlos ist.
Für diesen Beitrag haben gedankt: Christian213
|
|
Christian213 
      
Beiträge: 66
Erhaltene Danke: 3
Win XP, Win 7 64Bit
Lazarus 1.0.10
|
Verfasst: Di 22.10.13 15:32
So, ich konnte den "Übeltäter" einkreisen und den Fehler fixen.
Der originale Codeblock sah so aus:
Delphi-Quelltext 1: 2: 3: 4:
| StringList := TStringList.Create; StringList.LoadFromFile('meinetollestringlist.ini'); StringList.Free; |
Ändere ich das "StringList.Free" in ein "FreeAndNil(StringList)", so fliegt keine Access Violation mehr?!
Das finde ich sehr erstaunlich, da "FreeAndNil" in der zuständigen Lib so definiert ist:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| procedure FreeAndNil(var obj); var temp: tobject; begin temp:=tobject(obj); pointer(obj):=nil; temp.free; end; |
Also im Grunde genommen wird dort ja nur zusätzlich der Pointer auf nil gesetzt.
Ist es grundsätzlich sinnvoll, immer FreeAndNil anstatt Free zu benutzen?
|
|
baumina
      
Beiträge: 305
Erhaltene Danke: 61
Win 7
Delphi 10.2 Tokyo Enterprise
|
Verfasst: Di 22.10.13 15:38
Ein FreeAndNil wird dann empfohlen, wenn danach eine Abfrage auf NIL bzw. Assigned auf dieses Object gemacht wird. Ansonsten reicht ein .Free völlig aus. Dass du durch Ersetzen von .Free auf FreeAndNil Erfolg hattest, erscheint mir äußerst suspekt.
Für diesen Beitrag haben gedankt: Christian213
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Di 22.10.13 15:58
baumina hat folgendes geschrieben : | Dass du durch Ersetzen von .Free auf FreeAndNil Erfolg hattest, erscheint mir äußerst suspekt. |
Wobei das immerhin ein Hinweis ist: irgendwo wird nach dem Freigeben noch auf die Stringlist zugegriffen.
Übrigens, falls das jetzt nicht nur beim Kopieren rausgekürzt war, fehlt da noch der Resourcenschutzblock (try-finally).
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Für diesen Beitrag haben gedankt: Christian213
|
|
Christian213 
      
Beiträge: 66
Erhaltene Danke: 3
Win XP, Win 7 64Bit
Lazarus 1.0.10
|
Verfasst: Di 22.10.13 16:14
Hallo,
ja, das "try...finally" habe ich in der Tat raus gekürzt.
Kurioserweise greift mein Code nirgends mehr auf "StringList" zu, ich könnte mir nur vorstellen dass dies irgendwo in der "Abräumroutine" der RTL deklariert ist, dass alle "nicht-NIL" Objekte beim Beenden der Anwendung freigegeben werden und es in diesem Fall knallt, da das Objekt bereits freigegeben wurde.
Was dagegen spricht ist, dass es nur bei diesem Objekt überhaupt derartige Probleme gibt.
Vielleicht ein Bug im Lazarus? Wer weiß... Ich für meinen Teil bin damit zufrieden, dass es nun klappt und ich auch kein Memoryleak habe.
|
|
baumina
      
Beiträge: 305
Erhaltene Danke: 61
Win 7
Delphi 10.2 Tokyo Enterprise
|
Verfasst: Di 22.10.13 16:26
Dass etwas automatisch freigegeben wird, ohne dass es einen Owner hat, schließe ich aus.
Ich freue mich für dich, dass es jetzt ohne Fehler klappt, trotzdem möchte ich dir den Schwung etwas rausnehmen, indem ich dir sagen muss, dass es sehr gut sein kann, dass wenn du nun irgendwo ein paar Zeilen dazu programmierst, es danach wieder an irgendeiner Stelle zum Absturz kommt. Deswegen solltest du dir doch nochmal die Zeit nehmen und den Fehler genauer herausfinden.
Ich kenne solche seltsamen, nicht wirklich nachvollziehbaren sporadischen Abstürze (bzw. Accessviolations) bei der Threadprogrammierung sehr gut, die haben mich des Öfteren fast zum Wahnsinn getrieben.
|
|
hathor
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Di 22.10.13 16:27
Nach dem "StringList.LoadFromFile" wird nichts geändert und gespeichert?
|
|
Christian213 
      
Beiträge: 66
Erhaltene Danke: 3
Win XP, Win 7 64Bit
Lazarus 1.0.10
|
Verfasst: Mi 23.10.13 07:52
|
|
WasWeißDennIch
      
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: Mi 23.10.13 09:22
Und diese wird nicht zufällig durch z.B. irgendwelche Seiteneffekte noch einmal durchlaufen?
|
|
Christian213 
      
Beiträge: 66
Erhaltene Danke: 3
Win XP, Win 7 64Bit
Lazarus 1.0.10
|
Verfasst: Mi 23.10.13 09:44
WasWeißDennIch hat folgendes geschrieben : | Und diese wird nicht zufällig durch z.B. irgendwelche Seiteneffekte noch einmal durchlaufen? |
Die Methode wird mehrfach in der Anwendung benutzt, allerdings jedesmal mit anderen Stringlisten als Parameter. Diese eine Stringlist, die den AV verursacht hat, wird nur 1x im gesamten Code erzeugt, gefüllt, verarbeitet und wieder freigegeben. Daher wundere ich mich ja auch, dass es beim Beenden zu dem Fehler kam.
|
|
Nersgatt
      
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Mi 23.10.13 10:33
Ich würde alle Stringlisten mit FreeAndNil freigeben. Und außerdem am Anfang der Procedure mal irgendwie sowas einfügen:
Delphi-Quelltext 1:
| Assert(Assigned(DeineListe), 'ups') |
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
WasWeißDennIch
      
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: Mi 23.10.13 11:06
Das wäre aber eher Herumdoktern an den Symptomen statt Bekämpfung der Ursache. Lediglich lokal benutzte Objekte werden auch nur lokal deklariert, erzeugt und freigegeben. Global benutzte erzeugt man nur einmal und gibt sie auch nur einmal frei, üblicherweise bei Programmende. Wer mag, kann ja Lazy Initialization nutzen, dann wird auch nur der tatsächlich benötigte Speicher belegt.
|
|
Christian213 
      
Beiträge: 66
Erhaltene Danke: 3
Win XP, Win 7 64Bit
Lazarus 1.0.10
|
Verfasst: Mi 23.10.13 11:51
@Jens: Ich bin mir ziemlich sicher, dass die Methode nie mit einem nicht-definierten Objekt aufgerufen wird, da es sonst eine Exception geben würde.
Aber testen kann ich es ja trotzdem mal.
|
|
Nersgatt
      
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Mi 23.10.13 12:00
Christian213 hat folgendes geschrieben : | @Jens: Ich bin mir ziemlich sicher, dass die Methode nie mit einem nicht-definierten Objekt aufgerufen wird, da es sonst eine Exception geben würde.
Aber testen kann ich es ja trotzdem mal. |
Nur wenn das Objekt nil ist. Sonst gibt es nicht zwingend ne Exception.
Mal ein Codebespiel:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| procedure TForm4.FormCreate(Sender: TObject); var ts : TStringList; begin
ts := TStringList.Create; ts.Free;
if ts.Count = 0 then begin MessageDlg('test', mtWarning, mbYesNo, 0); end;
end; |
Ob eine Exception auftritt oder der MsgDlg erscheint ist mehr oder weniger Zufall.
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
Christian213 
      
Beiträge: 66
Erhaltene Danke: 3
Win XP, Win 7 64Bit
Lazarus 1.0.10
|
Verfasst: Mi 23.10.13 13:59
Okay, das "Problem" hinter ".Free" habe ich verstanden: Man gibt zwar den allozierten Speicher wieder frei, der "Zeiger" dorthin bleibt aber gesetzt.
Umso mehr frage ich mich, warum dann nicht auf Nummer sicher gehen und immer FreeAndNil verwenden? Was spricht denn gegen FreeAndNil?
|
|