![]() |
Fehlertoleranz DELPHI Compiler
Anbei mal ein simples Codebeispiel mit einem Fatalen Bug
Delphi-Quellcode:
TDataClass = class (...) ..... end; TForm1 = class(TForm) aDataClass : TDataClass; end; TForm2 = class(TForm) bDataClass : TDataClass; end; procedure aForm1.Doit (....); begin try aForm2.Create(...) aDataClass := aForm2.bdataclass; // gemeinsame Daten :-) finally aForm2.Free end; aDataClass.Auswerten(...); end; aDataClass.Auswerten(...); ist je eigentlich nil nachdem ich aForm2.Free ausgeführt habe. Trotzdem ging bei mir der Aufruf oft gut ( .... >> 100 x ), vermutlich war der Speicher noch nicht überschrieben , aber halt nicht immer. Den Fehler haben wir erst nach langer Suche entdeckt, weil der Code ja eigentlich gemacht hat was er sollte und dort hat niemend einen Fehler mehr vermutet. Kann ich mein Projekt so coompilieren eine strenge Speicherverwaltung erzwingen damit solche Fehler schnell auffallen ? |
AW: Fehlertoleranz DELPHI Compiler
obj.Free setzt nicht auf nil ... es gibt nur frei.
![]() Und das obj.Create ist theoretisch auch korrekt (wenn man es richtig nutzt) ... es initialisiert aber nur eine bestehende Instanz und reserviert keinen Speicher. |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Der Speichermanager kann dann z.B. beim Freigeben den Speicher mit einem speziellen Muster überschrieben, und kriegt dann damit später mit wenn der Prozess versucht auf diesen Speicher zuzugreifen. Solche Tools gehören eigentlich zum Standard wenn man seine Software vernünftig Testen will. |
AW: Fehlertoleranz DELPHI Compiler
@Bernhard
Ein Bug ist es nicht, eher ein konzeptionelles Problem. Hier (und an anderen Stellen) wurde das schon mal diskutiert: ![]() Das neu eingeführte Referenzcounting für Objekte (siehe aktueller Thread) könnte evtl. etwas Abhilfe schaffen. Allerdings würde dann das Objekt noch im Speicher bleiben, obwohl es eigentlich bereits aufgelöst sein sollte. Im Grunde würde das Überschreiben des Speichers verhindern und Deine "Glückstreffer" sicher gestalten. Es würde also nicht "knallen", aber Du würdst mit einem Objekt arbeiten, das eigentlich gar nicht mehr da ist. Um den Verweis zu nilen braucht es irgendeinen Observer, der standardmäßig im Delphi leider nicht existiert. |
AW: Fehlertoleranz DELPHI Compiler
@ Stali :
mit eigentlich ungültigen Werte noch arbeiten zu können halte ich für nicht sehr geschickt, man sollte das Problem an der Wurzel packen und auch dort sofort beheben. FASTMM4 und Madshi können zwar nach dem Auftreten solle Probleme anzeigen, ich fände es aber eine bessere Lösung wenn mann beim Übersetzen auf solche "Handwerklichen" Fehler den Code zu untersuchen könnte |
AW: Fehlertoleranz DELPHI Compiler
Und das der Aufruf "zufällig" gut geht liegt daran,
daß der Objekt-Zeiger auf einen Speicher zeigt, wo ein passendes Objekt mal war. Außerdem gibt FastMM Speicher nicht immer sofort wieder frei. Es scheint sonst noch niemand zwischenzeitlich in diese Speicherblöcke reingeschrieben zu haben. Und "vorallem" du machst in deinen "billigen" Objekten nicht viel ... vorallem nicht im Donstructor und Destructor, sonst hätte das falsche Create bei dir auch schon geknallt. Zitat:
Wenn dann so der Speicher der Objekte wirklich weg ist, dann knallt es eher mal. [edit] Daß obj.Create bei dir zufällig funktioniert, lag wohl eher daran, daß du hier eine böse globale Variable nutzt, welche mit nil initialisiert war. |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Wie lange soll denn der Compiler auf dem Code rumnudeln bis er alle möglichen handwerklichen Fehler gefunden hat? Das ist ein klarer Fall für einen Test mit DUnit aber nicht für den Compiler |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Mit FastMM findet man rein praktisch 99.99% aller Probleme. Was willst Du mehr? |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Wenn ein Dritter mein Fahrrad klaut kann ich mich am nächsten Tag nicht mehr drauf setzen. Wenn irgendwer mein Objekt zerstört, dann sind die Speicherzellen halt ja noch dort und irgendwelche Zeiger können auf die Stelle verweisen. Eine vollständig automatische Zeigerbereinigung ist undenkbar - nicht mal für mich! :stupid: Aber wenn ich dem Compiler sagen könnte: "Richte mal für mich eine Überwachung meines Fahrrades ein und setze F1 und F2 auf null, wenn das Fahrrad aufgelöst wird.", das wäre schon eine nette Sache und würde dem Programmierer eigene Obser-Lösungen oftmals abnehmen können. Natürlich hilft das Konstrukt auch nur dann, wenn das Fahrrad regulär freigegeben wird, aber nicht, wenn eine Walze drüber fährt (bzw. der Speicher irgendwie fehlerhaft überschrieben wird). |
AW: Fehlertoleranz DELPHI Compiler
Klassischer Fall von Objekt über seine lifetime hinaus benutzt.
Will man dem entgehen, muss man entweder genau Herr darüber sein, wann wer was frei gibt oder man wendet sich einer GC Sprache zu, da kann man anders rumfuhrwerken. In deinem konkreten Fall würde ich aber eher ein Architektur Problem vermuten, da dein TForm2 das TDataClass Objekt erzeugt, während TForm1 das scheinbar nicht macht. Wenn doch, dann hast du an der gezeigten Stelle nicht nur einen dangling Pointer sondern auch ein Memory leak. An dieser Stelle steht dann die Entscheidung, ob du exakt dieselbe Instanz brauchst, oder ob dort ein Assign Aufruf reicht, um bloß den State von dem einen TDataClass Objekt ins andere zu schieben. |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Verbirgt sich dahinter eigentlich praktische Technik, oder liegt hier die Betonung eher auf "rumfuhrwerken"?? |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Einziger Fallstrick dabei ist, wenn man den Code später auf Interfaces umstellt. An FreeAndNil kann man nämlich auch Interfaces übergeben, deshalb knallt es leider erst beim Aufruf. |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
|
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Zitat:
Delphi-Quellcode:
garniert) kaschiert nur noch mehr eine mögliche Ahnungslosigkeit, wer für diese Instanz eigentlich zuständig ist.
if Assigned(...)
Aber lasst uns die FreeAndNil Diskussion gleich wieder begraben, das hatten wir schon ![]() |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
|
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Wem gehört die Instanz? Wir haben es hier mit einem globalen Objekt zu tun, quasi ein Singleton, der seine Singularität durch das Kopieren der Instanz erhält. Wir wissen, das so etwas ziemlich 'böse' ist (eigentlich ist es nur ein sehr wackeliges Design) und mit Samthandschuhen anzufassen, ach was rede ich, komplett zu vermeiden ist. Wozu gibt es in Delphi das Konzept der Datenmodule? Die sind ja nicht auf TDataSets beschränkt. Das Konzept ist 25 Jahre alt (soweit ich mich erinnere) und sollte vermeiden, das Daten wüst hin und her verschoben und übergeben werden. Letztendlich verwaltet dann ein Datenmodul die DataClass-Instanz, erzeugt sie und gibt sie wieder frei. Es muss -per Architektur- einfach wissen, wann es sich verabschieden darf. Und wenn man dann auch noch damit aufhört, Objektinstanzen zu kopieren, sondern nur als Parameter zu übergeben, kann der beschriebene Bug (der ja keiner is) gar nicht auftreten. |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Die Korrektheit eines (Turing-)Programms zu prüfen kann (je nach genauer Aufgabe) schwer bis unmöglich (Halteproblem) sein. Es gibt allerdings durchaus auch brauchbare statische Analysemethoden für Quellcode. Als einfaches bekanntes Beispiel: Typprüfung. Sei etwas vorsichtiger mit Begriffen wie "NP-vollständig". |
AW: Fehlertoleranz DELPHI Compiler
Wann kommt endlich neben dem Syntaxcheck auch der Semantikcheck?:stupid:
|
AW: Fehlertoleranz DELPHI Compiler
Zitat:
|
AW: Fehlertoleranz DELPHI Compiler
Zitat:
|
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Ich sehe den Vorteil eher auf der nicht-GC Seite. Dort kann man konkret testen und debuggen. Während mit GC einfach jedes tote Objekt lustig weiter verwendet werden kann. Dieses "sich keinen Kopf machen müssen" mit GC halte ich für eine reichlich naive Sichtweise. |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Aber stimmt ... noch ein Grund, warum der "Probleme" bereiten kann. Und die hatte ich sogar schon lange bevor das hier auftauchte: ![]() |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
|
AW: Fehlertoleranz DELPHI Compiler
Ein GC räumt erst dann Objekte weg, wenn alle Referenzen die auf sie verweisen aus ihrem Scope fallen. Zugriffe auf Referenzen die nicht im Scope liegen, sind schon durch den simplen Syntax-Check abgedeckt.
|
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Ab dann sind Zugriffe auf das Objekt von anderer Stelle aus (Referenzen) darauf grundsätzlich fehlerhaft. Entweder werden a) vor kurzem noch korrekte Daten benutzt, die inzwischen aber im Projektkontext keine Gültigkeit mehr haben b) komplett ungültige Daten benutzt, weil die Speicherstellen inzwischen überschrieben wurden c) knallt es weil Methoden auf Grund eines überschriebenen Speichers nicht mehr ausführbar sind Ein GC sichert ab, dass nur Problem a) entsteht. Aber eine ausreichende Lösung ist das noch nicht. Im Spiel könnte so noch auf das Raumschiff zugeriffen werden, obwohl es eigentlich schon explodiert ist. Jetzt kann man streiten ob das besser ist als eine Exception. |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
![]() Außerdem ist der GC etwas schlauer als das simple RefCounting, was COM Interfaces praktizieren (zirkuläre strong references = memleak). |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
nassem Beton repräsentiert - der in deinen Vorgarten gekippt werden soll. Jetzt willst Du dieses Objekt löschen - und alle Zugriffe auf das Objekt sollen zu Fehlern führen. Mit GC ist das ein Krampf - man muss da quasi händisch das Rad der Access Violation neu erfinden. Meiner Erfahrung nach sind die QS-Werkzeuge auf der Seite des GC einfach wesentlich schlechter. Dass manche Leute gar nicht erst auf die Idee kommen warum man mal Objekte als erledigt markieren will spricht Bände... |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
|
AW: Fehlertoleranz DELPHI Compiler
Klar, man kann ja gerne ein "Erledigt" Flag in seine Klassen einbauen.
Das muß dann aber auch entweder extern überall geprüft werden, oder man prüft es intern und baut in jede Methode, Getter und Setter die Prüfung ein. (früher reichte es mal, wenn das Objekt dA freigegeben wurde, wo man Free aufrief. :stupid: ) |
AW: Fehlertoleranz DELPHI Compiler
Überhaupt habe ich den Eindruck, dass ein GC fast nur dann zu diesen ominösen Problemen führen kann, wenn man einen Speichermanager fälschlicherweise missbraucht um Geschäftslogik zu implementieren.
|
AW: Fehlertoleranz DELPHI Compiler
Das Problem schient so wichtig zu sein, dass man es hier paralell in mehreren Threads diskutiert!
|
AW: Fehlertoleranz DELPHI Compiler
Zitat:
|
AW: Fehlertoleranz DELPHI Compiler
Zitat:
[add] Post #22 |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
P.S. Wobei das Problem bei ARC nicht aufgetreten wäre :twisted: |
AW: Fehlertoleranz DELPHI Compiler
Das mit der GC Problematik meinte ich. Aber meine Meinung ist ja nicht relevant.
|
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Mir scheint hier irgendwie teilweise das Bild vorzuherrschen, Garbage Collectors wären dazu da, schlechten Programmierstil zu kaschieren oder ähnliches und mit genügend "Skill" hätte man soetwas nicht nötig. Das ist, gelinde gesagt, völliger Schwachsinn. |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
|
AW: Fehlertoleranz DELPHI Compiler
Vielleicht können wir uns darauf einigen, dass es um vergessene Referenzen auf aufgelöste Objekt(speicherplätz)e geht.
Solche Zugriffe sollten sowohl von der BL als auch von der GUI vermieden werden. Dennoch kann es vorkommen, insbesondere bei der Entwicklung eines komplexen Projektes. Man braucht ja nur den Owner aufzulösen und vergisst, dass das Objekt diesem zugeordnet ist. Schön wäre es, wenn alle Referenzen auf ein Objekt bei dessen Auflösung automatisch genilt würden, aber das wird auch in Zukunft nicht realisierbar sein. Mit Assign(Referenz) könnte man immer auf die Existenz des Objektes prüfen. Die Objektreferenz noch am Leben zu erhalten und damit auf fragwürdige Daten zuzugreifen finde ich eigentlich nicht optimal. Es hat eben Vor- und Nachteile. Sofern man mit den Daten nix weiter anstellt, ist das für das Projekt nicht weiter schädlich. Wenn die Existenz des Objektes aber mein übriges Projekt beeinflusst, dann ist das natürlich schlecht. Also sollte man ein Objekt vor dem Auflösen "disablen", so können vergessene Referenzen erkennen, dass das Objekt, auf welches sie zeigen eigentlich nicht mehr da ist. Ein prakisches Beispiel ist Windows: Führt man in einem Control ein Schließen seines ParentForms durch, erhält dieses Control evtl. zuvor noch den Focus. Winndows will das Control kurz darauf schön focussiert zeichnen. Form und Control sind nicht mehr da - es knallt. (Das hängt wohl tatsächlich mit den Handels zusammen, ist aber ein anschauliches Beispiel.) |
AW: Fehlertoleranz DELPHI Compiler
Zitat:
Dann gibt es auf einmal in Programmen überhaupt keine Notwendigkeit mehr auf Zugriffe auf ungültige Objekte überhaupt zu testen "wegen der Geschäftslogik in realen Softwaresystem". Als nächstes kommt ihr noch auf die Idee und schreibt für C# einen Meta-GC, der verhindert, dass dort Weak-References jemals auf ungültige Targets verweisen können... Comedy vom feinsten... |
AW: Fehlertoleranz DELPHI Compiler
Ab TComponent könnte man aber schon seit Jahrzehnten Listen erstellen (hat nur noch keiner gemacht), wo die Referenzen automatisch entfernt werden, wenn jemand Anderes (z.B. der Owner) das Objekt freigibt.
Also ab TComponent ist dieses Feature drin, wenn man es nicht selber implementieren will. Dieses wird z.B. für die ganzen (Kreuz)Verlinkungen ala Parent-Child und Owner-Owned verwendet. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:58 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz