![]() |
Freigeben -> AV; Bearbeiten -> Nichts weiter
Hallo ihr,
ich habe ein seltsames Problem mit einem Schulprojekt. Und zwar habe ich eine von TObjectList abgeleitete Liste welche Objekte vom Typ TExemplarZeile beinhalten. Diese hat als Vorfahre die Klasse TZeile. Jetzt gibt es noch eine zweite ListenKlasse welche Objekte von Typus TKursZeile aufnimmt. Wenn ich nun die erste mit Daten fülle, kann ich nicht mehr das erste Objekt der zweiten Liste löschen oder freigeben. Weil dann kommt mir einen AV entgegen. Ansonsten kann ich damit arbeiten wie ich will. Wenn ich sogar das Objekt vorher entferne und dann die andere Liste befülle funktioniert alles wunderbar.
Delphi-Quellcode:
Das macht "Exemplarliste.LadeAusDatenbank":
Kursliste.LadeAusDatenbank(Datenbank);
// An dieser Stelle funktionieren folgende Aufrufe (natürlich nicht alle gleichzeitig) Kursliste[0].Free; // Oder Kursliste.Delete(0); Exemplarliste.LadeAusDatenbank(Datenbank); // Hier aber nicht mehr Kursliste[0].Free; // Oder Kursliste.Delete(0);
Delphi-Quellcode:
Ich versuche weiterhin das weiter einzugrenzen, aber im Moment möchte ich euch nicht den ganzen Sourcecode geben. Aber vielleicht hilft euch das schon? Besonders, warum man nicht freigeben kann, aber beispielsweise Werte ändern kann.
procedure TExemplareZeilenListe.LadeAusDatenbank(const ADatenbank : TDatenbank);
var Daten : TExemplarZeile; i : Integer; ILExemplarIDs, ILPersonenIDs, ILBuchIDs, ILBEIDs : TIntegerList; BLAusgedruckt : TBooleanList; begin Clear; ILExemplarIDs := TIntegerList.Create; ILPersonenIDs := TIntegerList.Create; ILBuchIDs := TIntegerList.Create; ILBEIDs := TIntegerList.Create; BLAusgedruckt := TBooleanList.Create; try ADatenbank.GibExemplarIDs(ILExemplarIDs); ADatenbank.IstAusgedruckt(BLAusgedruckt, ILExemplarIDs); ADatenbank.GibBuchIDs(ILBuchIDs, ILExemplarIDs); ADatenbank.GibBEIDs(ILBEIDs, ILExemplarIDs); ADatenbank.GibPersonen(ILPersonenIDs, ILExemplarIDs); for i := 0 to ILExemplarIDs.Count - 1 do begin Daten := TExemplarZeile.Create; Daten.ID := ILExemplarIDs[i]; Daten.Ausgeliehen := nil; Daten.Buch := nil; Daten.VPersonenID := ILPersonenIDs[i]; Daten.VBuchID := ILBuchIDs[i]; Daten.BEID := ILBEIDs[i]; Daten.Ausgedruckt := BLAusgedruckt[i]; Add(Daten); end; finally ILExemplarIDs.Free; ILPersonenIDs.Free; ILBuchIDs.Free; ILBEIDs.Free; BLAusgedruckt.Free; end; end; MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Was mir spontan dazu einfällt ohne genauer in den Quelltext geschaut zu haben ist die Eigenschaft OwnsObjects. Wenn du die Objekte selbst freigeben willst, etc., dann musst du die auf False setzen. Wenn die TObjectList bei einem Delete oder der Zerstörung der Liste diese selbst aus dem Speicher entfernen soll, dann muss das auf True stehen.
|
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Sebastian,
OwnObjects ist bei diesen Listen auf true. Aber daran dürfte es nicht liegen, wobei ich da misstrauisch geworden bin :mrgreen: Naja ich habe mich mal daran gesetzt den Code zu entschlacken. Und das Ergebnis hänge ich einfach mal an! Übrigens ist der Fehler (oder ein anderer?) wo anders. Ich bin der Sache mal auf der Spur. [edit]Damit ihr wisst wo ihr suchen solltet: Der Ursprüngliche Fehler tritt in Zeile 39 in UHaupt.pas auf. Der neue Fehler (oder vielleicht zeigt sich der gleiche einfach anders) in Zeile 207 in UZeilen.pas bzw die Fehlermeldung erscheint, wenn man mit dem Debuger gerade über Zeile 2136 läuft.[/edit] [edit]Ich bitte alle die es sich bereits heruntergeladen haben, die "db"-Datei zu löschen und durch diese hier im Anhang zu ersetzen.[/edit] [edit]Folgendes: In der Schule unter Delphi 2009 --> Fehler beim Zerstören In der Schule unter Turbo Delphi --> Fehler beim Erstellen aber keinen beim Zerstören des KursEintrages (Z. 39 UHaupt.pas) --> Zerstören der BLAusgedruckt-Liste (Z. 229 UZeilen.pas)[/edit] MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Hallo,
wenn ich die alte Variante von meiner UIntegerList/UBooleanList verwende, bekomme ich keine Fehler. Also vermute ich die Probleme dort. Aber ich weiß nicht, was ich da falsch mache. MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Ich möchte ja nicht stören, wüsste aber gerne, was ich da verbrochen habe :)
MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Hi,
vielleicht bin ich ja völlig auf dem falschen Dampfer, aber dein Code muss eine Exception werfen, wenn OwnObjects True ist:
Delphi-Quellcode:
Aus TObjectList:
Kursliste[0].Free;
// Oder Kursliste.Delete(0);
Delphi-Quellcode:
TObject(Ptr) hast du ja schon freigegeben.
procedure TObjectList.Notify(Ptr: Pointer; Action: TListNotification);
begin if OwnsObjects then if Action = lnDeleted then TObject(Ptr).Free; inherited Notify(Ptr, Action); end; Cu, Frank |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Das habe ich oben ja auch schon geschrieben und das passiert ja auch. Wenn die Zeile drin ist, dann gibts den Fehler wie nicht anders zu erwarten beim Zerstören von Kursliste.
Wenn dieser Fehler nicht drin ist, dann tritt der Fehler laut Callstack und Debuggen im CPU-Fenster beim Finalisieren einer Unit auf. Genaueres hab ich jetzt eben noch nicht gesehen. // EDIT: Um genau zu sein tritt der Fehler in DoneControls auf, aber: Es werden auch einige Speicherlecks gemeldet... Um die solltest du dich einmal kümmern, Details sagt dir z.B. FastMM. |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Hallo Sebastian und Frank,
Zitat:
Zitat:
Zitat:
Und wie gesagt. Es kann zufall sein, dass er nur mit der Integer-/BooleanList im Anhang auftritt. MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Im Anhang liegt der entsprechende delphiinterne Report. |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Liste der Anhänge anzeigen (Anzahl: 2)
Tach Sebastian,
aber das hat dann nichts mehr mit freigeben zu tun. Weil folgender Code funktioniert:
Delphi-Quellcode:
Deshalb denke ich, dass irgendwo in der TIntegerList, TBooleanList oder TMainList sein müsste (ich wiederhole mich :) ). Ich hänge einfach die alte Version der TInteger-/TBooleanList an, womit es funktioniert. (Kleiner Hinweis: UMainList dann in der UDatenbank entfernen)
procedure TFLiberate.FormCreate(Sender: TObject);
begin Datenbank := TDatenbank.Create(ExtractFilePath(ParamStr(0)) + 'db'); ReportMemoryLeaksOnShutdown := true; Exemplarliste := TExemplareZeilenListe.Create; Kursliste := TKursZeilenListe.Create; Kursliste.LadeAusDatenbank(Datenbank); Exemplarliste.LadeAusDatenbank(Datenbank); end; procedure TFLiberate.FormDestroy(Sender: TObject); begin Exemplarliste.Free; Kursliste.Free; Datenbank.Free; end; MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Hi,
ich hatte schon 2, 3 Male ein ähnliches Phänomen. Selbst bei noch eindeutigeren "Nicht-Fehlern" im Code :) eine Funktion hat eine Exception geworfen. Zum prüfen habe ich folgende Änderungen im Code vorgenommen:
Delphi-Quellcode:
danach kam die Exception nicht mehr.
function XYZ;
var s: string; // geändert i: Integer; begin [...] s := IntToStr(i); // geändert [...] ShowMessage(s); // geändert [...] end; Auskommentieren dieser Zeilen hat die Exception zurückgebracht. Bei mir im Betrieb waren alle Entwickler ratlos. Also habe ich mal alle *.dcu und ProjektName.* außer der Projektname.dpr. Danach Projekt neu erzeugt und alles hat wieder einwandfrei funktioniert. Vielleicht verschluckt sich Delphi bei dir auch an irgend wo einer Kleinigkeit. Gruß Ansgar |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Das sind meistens alles Symptome für Speicherprobleme. Leider tritt, wenn man den falschen Speicher überschreibt z.B., der Fehler oft am anderen Ende des Projekts auf und ist dort unerklärlich.
Und leider ist so etwas nicht unbedingt reproduzierbar. Und leider können ganz andere Änderungen, die eigentlich nichts damit zu tun haben das Problem verdecken. Sei es eine zusätzliche Variable, ein zusätzlicher Befehl, der eigentlich gar nix macht, oder sowas. Eine nicht computerbezogene Erklärung habe ich dazu einmal hier geschrieben: ![]() Deshalb sind die Hinweise von Delphi auf Speicherlecks sehr Ernst zu nehmen. Irgendetwas stimmt da definitiv nicht. Ich habe nur gerade bis eben an einem eigenen Projekt gearbeitet, so dass ich mich da nicht drum kümmern konnte. Ich schau es mir gleich noch einmal genauer an. |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Zitat:
Zitat:
Da der Fehler auftritt, wenn ich eine von beiden neuen Listen verwende, glaube ich, liegt es an der TMainList. MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Was mir auffällt:
Delphi-Quellcode:
Müsste nicht in beiden Fällen Speicher freigegeben werden?
procedure TMainList.Clear;
begin FCount := 0; case FReserveManagment of rmExact, rmProgressive: begin FReservedLength := 0; end; end; end; und destructor TMainList.Destroy; begin inherited; end; |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Also ich bin mir da nicht so sicher. Bei Clear sollte ich das Array wirklich einmal resizen. Aber ansonsten ist das ja ein dynamisches array von TVarRec, welches ja an sich automatisch "freigegeben" wird?
MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Naja, was heißt automatisch freigegeben, ich sehe kein SetLength(..., 0), und das müsste da eigentlich irgendwo stehen. Denn sonst gibts doch erstmal noch eine Referenz auf die Objekte und der Referenzzähler wird nach dem Freigeben der Liste nicht 0. Denn deren Freigabe zerstört zwar die Referenzen, aber das passiert ja nicht explizit.
Ich hab gerade mal in die Implementierung von TList geschaut, daran solltest du dich vielleicht orientieren:
Delphi-Quellcode:
Das heißt da wird durchaus explizit gelöscht.
destructor TList.Destroy;
begin Clear; end; procedure TList.Clear; begin SetCount(0); SetCapacity(0); end; Sicher bin ich mir über die Interna der Referenzzählung aber aus dem Kopf auch nicht, da schaue ich immer in den generierten Assemblercode, wenn ich nicht sicher bin. ;-) |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Okay habe ich eigentlich auch:
Delphi-Quellcode:
Aber es kommt weiterhin der Fehler und die Speicherlecks sind weiterhin vorhanden.
destructor TMainList.Destroy;
begin Clear; ResizeArray(0); inherited; end; procedure TMainList.Clear; begin FCount := 0; case FReserveManagment of rmExact, rmProgressive: begin FReservedLength := 0; end; end; ResizeArray(FReservedLength); end; MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Um genauer zu schauen wo die Speicherlecks liegen solltest du dir einmal FastMM anschauen und die Überprüfung aktivieren.
Und dann gibts da noch madExcept oder EurekaLog, die zum Debuggen sehr gut sind. |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Hi,
mir ist beim Lesen auch noch etwas aufgefallen:
Delphi-Quellcode:
procedure TIntegerListList.Delete(const AIndex: Integer);
begin // next condition can not be True - or statt and // genau so in: TIntegerListList.GetIntegerLists if (AIndex < 0) and (AIndex >= Count) then raise EIntegerListListError.CreateFmt(SListIndexError, [AIndex]); FIntegerLists.Delete(AIndex); FHeadInteger.Delete(AIndex); Dec(FCount); end;
Delphi-Quellcode:
Kannst du garantieren, dass AIndexes absteigend sortiert ist? Sonst löscht du die falschen oder bist plötzlich ausserhalb des Bereiches.
procedure TZeilenListe.Delete(AIndexes: TIntegerList);
var i: Integer; begin for i := 0 to AIndexes.Count - 1 do Delete(AIndexes[i]); end; Cu, Frank |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Ah okay.
Normalerweise denke ich immer an die Rückwärtige Schleife. Ich probiere es gleich mal aus! Aber du meinst die falsche TIntegerListList ^^, aber wenn ich die Schleife in UZeilen umdrehe hat das keine Auswirkung. Und wie sieht es mit FastMM aus? Also ich habe mir jetzt FastMM4 gedownloadet, aber wie erhalte ich genauere Angaben? MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Zitat:
Schau mal in die FastMM4Options.inc, da gibts nen paar Optionen. Bei Speicherlecks sollte eine Logdatei automatisch erstellt werden, wenn du das Programm im Debugger startest. Wenn das Log auch außerhalb von Delphi erstellt werden soll, gibts da glaube ich die Option RequireDebuggerPresentForLeakReport oder so. |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Naja ich hatte es gedownloadet und entpackt und zu der Form sowie zum Projekt (an erster Stelle) "FastMM4" hinzugefügt. Aber weiter kam ich dann halt nicht.
MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo ihr,
ich habe es nun hingekriegt die Fehlermeldung auszugeben. Das Ergebnis ist im Anhang. Eine Sache: Ich habe den Code etwas verändert. In UHaupt:
Delphi-Quellcode:
In UZeilen:
procedure TFLiberate.FormCreate(Sender: TObject);
begin Datenbank := TDatenbank.Create(ExtractFilePath(ParamStr(0)) + 'db'); ReportMemoryLeaksOnShutdown := true; Exemplarliste := TExemplareZeilenListe.Create; Kursliste := TKursZeilenListe.Create; Exemplarliste.LadeAusDatenbank(Datenbank); end;
Delphi-Quellcode:
Wenn ich den letzten Datenbank Aufruf auch noch entfernen würde, würde kein Fehler mehr kommen.
procedure TExemplareZeilenListe.LadeAusDatenbank(const ADatenbank : TDatenbank);
var Daten : TExemplarZeile; i : Integer; ILExemplarIDs, ILPersonenIDs, ILBuchIDs, ILBEIDs : TIntegerList; BLAusgedruckt : TBooleanList; begin inherited; ILExemplarIDs := TIntegerList.Create; ILPersonenIDs := TIntegerList.Create; ILBuchIDs := TIntegerList.Create; ILBEIDs := TIntegerList.Create; BLAusgedruckt := TBooleanList.Create; try ADatenbank.GibExemplarIDs(ILExemplarIDs); finally ILExemplarIDs.Free; ILPersonenIDs.Free; ILBuchIDs.Free; ILBEIDs.Free; BLAusgedruckt.Free; end; end; MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Die Leaks sind zwar unschön, aber schlecht sind vor allem die ersten beiden Fehler. Diese verursachen auch die Fehler. Diese zeigen, dass da Speicher tatsächlich überschrieben wird.
Jetzt müsste der Stacktrace mit den Adressen verglichen werden, dafür gibts auch die Möglichkeit eine Map Datei zu erzeugen. Ob FastMM da noch mehr Details kann, weiß ich nicht. |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Hallo Sebastian,
ich vermute es liegt am ehesten an den "Move"-Befehlen. Ich werde mal gucken, in wie fern ich da was rausbekomme. Okay, ich habe alle "Move"-Befehle manuell implementiert (in TMainList!) und es funktioniert. Wobei nur das "Move" in "MoveContent" aufgerufen wird:
Delphi-Quellcode:
So funktioniert es. Wenn ich aber die Kommentarezeichen entferne, bekomme ich Fehler.
procedure TMainList.MoveContent(const AFrom, ATo, ASize: Integer);
var i, ValueCount: Integer; ValueBuffer : TVarRecArray; begin // Problemstelle ! //Move(FValues[AFrom], FValues[ATo], ASize); //exit; ValueCount := ASize div SizeOf(TVarRec); SetLength(ValueBuffer, ValueCount); for i := 0 to ValueCount - 1 do ValueBuffer[i] := FValues[AFrom + i]; for i := 0 to ValueCount - 1 do FValues[ATo + i] := ValueBuffer[i]; end; Ich verwende "Move" auch im Add(TVarRecArray) sowie im Assign. Aber wie gesagt, das wird nicht aufgerufen in dieser Konstellation. Ich glaube ich werde das in eine extra "TXList"-Stressunit umbauen. Aber ist den hier nicht "Move" möglich? MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Doch, eigentlich geht das so. Prüf am besten vorher und nachher den Inhalt einmal im Debugger.
|
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Eigentlich ist der Inhalt immer in Ordnung.
MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo ich bins wieder,
ich habe jetzt hier eine "Stresstest"-Version. Bei mir kann ich nur IntegerList verwenden ohne Fehlermeldungen. MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Move sollte man in Verbindung mit Variablen deren Inhalt eine Referenzzählung erfordert nicht einsetzen.
Das betrifft insbesondere Daten die Strings oder Interface enthalten. |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Also alternativ jeden einzelnen Wert kopieren? Oder gibt es eine "bessere" Alternative?
MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Naja, wenn man sich das alles richtig überlegt, dann kann man das so auch machen, man muss aber dann genau schauen welchen Speichern man reserviert und freigibt usw., da müsste man sich dann selbst drum kümmern.
Wenn man nicht ein Array in der Länge verändert, sondern direkt den Speicher manipuliert, dann berührt dies den Referenzzähler nicht. Denn die alten Referenzen werden nicht entfernt sondern nur direkt inkl. Referenzzähler an die neue Stelle kopiert. Bei den alten wird der Speicher wieder freigegeben ohne, dass die Referenzzähler dekrementiert werden. Auf diese Weise sollte das klappen, ist aber noch mehr Handarbeit und man muss noch mehr aufpassen was man eigentlich tut. |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Das könnte so aussehen:
Delphi-Quellcode:
Die Grenzen des Arrays dürfen durch ASize natürlich nicht überschritten werden.
procedure MoveElements(var Source, Dest; ASize: DWord);
var BufferSize: DWord; Buffer, P1, P2: Pointer; begin P1 := @Dest; P2 := @Source; if DWord(@Source) < DWord(@Dest) then begin BufferSize := DWord(@Dest) - DWord(@Source); if BufferSize > ASize then BufferSize := ASize else Inc(DWord(P1), ASize - BufferSize); end else if DWord(@Source) > DWord(@Dest) then begin BufferSize := DWord(@Source) - DWord(@Dest); if BufferSize > ASize then BufferSize := ASize else Inc(DWord(P2), ASize - BufferSize); end else Exit; GetMem(Buffer, BufferSize); Move(P1^, Buffer^, BufferSize); Move(Source, Dest, ASize); Move(Buffer^, P2^, BufferSize); FreeMem(Buffer); end; procedure TMainList.MoveContent(const AFrom, ATo, ASize: Integer); begin MoveElements(FValues[AFrom], FValues[ATo], ASize); end; Deshalb würde ich MoveContent so ändern, dass ACount übergeben wird.
Delphi-Quellcode:
procedure TMainList.MoveContent(const AFrom, ATo, ACount: DWord);
begin {$ifdef DEBUG} if (AFrom + ACount > Length(FValues)) or (ATo + ACount > Length(FValues)) then raise Exception.Create('MoveContent -> Index überschreitet das Maximum'); {$endif} MoveElements(FValues[AFrom], FValues[ATo], ACount * SizeOf(FValues[0])); end; |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
Hi Blup,
Entschuldige das ich erst jetzt antworte, aber kann es sein, das du ASize als "Length" interpretiert hast? MfG xZise |
Re: Freigeben -> AV; Bearbeiten -> Nichts weiter
ASize wird in MoveElements als (Anzahl der zu verschiebenden Elemente) * SizeOf(Element) angenommen.
Ich habe aber vorgeschlagen MoveContent so zu verändern, dass nur die Anzahl der Elemente übergeben wird. Das ist für die Verwendung einfacher und an einer zentralen Stelle kann so leicht die Überprüfung auf fehlerhafte Parametern erfolgen. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:16 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