AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Interfaces werden nicht dereferenziert

Ein Thema von xaromz · begonnen am 1. Jul 2005 · letzter Beitrag vom 1. Jul 2005
Antwort Antwort
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#1

Interfaces werden nicht dereferenziert

  Alt 1. Jul 2005, 10:44
Hallo zusammen,

ich hab ein kleines Problem mit meinem aktuellen Projekt: Um PlugIns unterstützen zu könne, habe ich meine Hauptobjekte in Interfaces umgebaut. Seit ich das gemacht habe hab ich ein riesengroßes Speicherleck (> 7MB nach dem Öffnen eines Dokuments). Meine Objekte sind hierarchisch organisiert (jedes Objekt besitzt einen Container (auch als Interface implementiert, Kapselung von TInterfaceList), in dem sich die Kind-Objekte befinden; das Ganze ist ähnlich wie bei nem TreeView).
Wenn ich ein Dokument erstelle (Hauptobjekt + ein Unterobjekt), dann ist beim Beenden der Referenzzähler des Hauptobjekts irgendwo zwichen 12 und 100, auch wenn ich nichts sont mach (da sollte doch dann immer der gleiche Wert stehen ).
Ich könnte jetzt natürlich überall schauen, wo ich das Objekt referenziere, aber da der Referenzzähler zwischendrinn auch mal > 1000 und mein Programm > 130.000 loc ist, sitze ich wahrscheinlich nächstes Jahr noch dran .

Darum Folgendes: Gibt es eine einfache Möglichkeit, solche offenen Referenzen zu finden oder muss ich wirklich nen Spaten nehmen und graben?

Danke für Antworten
xaromz
  Mit Zitat antworten Zitat
barf00s
(Gast)

n/a Beiträge
 
#2

Re: Interfaces werden nicht dereferenziert

  Alt 1. Jul 2005, 10:59
spateln ist immer gut


Delphi-Quellcode:
var
  xInterface: IWhatever;

begin
  if Assigned(xInterface) then
    xInterface := nil;
end;
es sei denn du machsts so

Delphi-Quellcode:
var
  xObj: TWhatever;

begin
  xObj := TWhatever.Create As IWhatever;
  try
  finally
    FreeAndNil(xObj);
  end;
end;
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#3

Re: Interfaces werden nicht dereferenziert

  Alt 1. Jul 2005, 13:38
Hallo,
Zitat von barf00s:
Delphi-Quellcode:
var
  xInterface: IWhatever;

begin
  if Assigned(xInterface) then
    xInterface := nil;
end;
Delphi-Quellcode:
var
  xObj: TWhatever;

begin
  xObj := TWhatever.Create As IWhatever;
  try
  finally
    FreeAndNil(xObj);
  end;
end;
Das sind beides leider keine Lösungen für mein Problem. Ich weiss, dass ich die Interfaces freigeben muss, nur nicht wo. Ich hab ein MDI-Fenster, das die Objekte erstellt und den PlugIns sowie diversen Paletten zur Verfügung stellt. Die Objekte werden in OnCreate erzeugt und in OnDestroy auf nil gesetzt. Irgendwo dazwischen werden an allen möglichen Stellen die Objekte verwendet bzw. manipuliert, das sind einige Hundert Stellen im Quellcode. So wie es aussieht muss ich dies Stellen alle untersuchen und die Interfaces da freigeben, wo ichs vergessen hab.
Als das noch einfache Delphi-Objekte waren war das ja egal, bzw. Delphi hat sogar gemeckert (Hinweis:...wird nie benutzt) wenn man das Objekt auf nil gesetzt hat. Warum bringt Delphi keine Warnung, wenn man ein Interface benutzt und nicht wieder freigibt?

Noch was: Wenn ich mir ein Interface so hole
Delphi-Quellcode:
if (XXX as IMyInterface).IrgendeinProperty = True then
  ...
wird dann das Interface von Delphi automatisch wieder freigegeben?

Gruß
xaromz
  Mit Zitat antworten Zitat
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#4

Re: Interfaces werden nicht dereferenziert

  Alt 1. Jul 2005, 14:16
Zitat von xaromz:
Noch was: Wenn ich mir ein Interface so hole
Delphi-Quellcode:
if (XXX as IMyInterface).IrgendeinProperty = True then
  ...
wird dann das Interface von Delphi automatisch wieder freigegeben?
Ja, das macht Delphi automatisch.
Ich kenne 3 Möglichkeiten dass die Referenzzählung nicht korrekt arbeitet:
1.) speichern von Interfacepointern in "normalen" Pointer.
Man muss mit AddRef nachhelfen, sonst wird das Objekt freigeben
Delphi-Quellcode:
var
   ob : Pointer;
   lg : ILogger;
begin
   lg := CoLogger.Create; // object erzeugen
   // RefCount ist 1
   lg._AddRef;
   // RefCount ist 2
   ob := lg;
end; // RefCount ist wieder 1
// Wenn nun der Pointer "ob" beschrieben wird ohne vorher ._Release aufzurufen
// hast du ein Speicherleck
2.) Manchmal implementiert man die Methoden _AddRef und _Release in eigenen Klassen, damit man
nicht von TInterfacedObject erben muss.
Wenn hier etwas falsch macht, ist die Referenzzählung ausser Kraft gesetzt -> Speicherleck
3.) Vermischung von Objekt- und Interfacereferenzen
Deine Objekte lassen sich in Objekt-Zeigern (abgeleitet von TObject) oder in Interfacezeigern
(abgeleitet von IUnknown) speichern.
Wenn man beim gleichen Objekt beide Arten anwendet, kommt man in Schwierigkeiten.
Andreas
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#5

Re: Interfaces werden nicht dereferenziert

  Alt 1. Jul 2005, 14:45
Hallo,

danke für die Antwort.
Ich erstelle natürlich ein Objekt und speichere es dann nur noch als Interface bzw. in einer TInterfaceList. Also sollte es keine Vermischung TObject <> IInterface bzw. Pointer <> IInterface geben.
Inzwischen bin ich auch dabei meine Referenzen zu überprüfen. Es ist doch erstaunlich, wie oft man so ein Objekt zuweist...

Aber wo wir schon dabei sind, wie sieht es eigentlich hiermit aus:
Delphi-Quellcode:
var
  I: IInterface;
  List: TInterfaceList;
  C: Integer;
begin
  for C := 0 to List.Count - 1 do
  begin
    I := List[C];
    // Tu was mit I
    // ***
  end;
end;
Muss bei *** ein I := nil; stehen oder erkennt Delphi hier die Zuweisung auf eine bereits "gefüllte" Variable und gibt das vorherige Interface frei? Ich vermute mal nein, oder?

Gruß
xaromz
  Mit Zitat antworten Zitat
Robert_G
(Gast)

n/a Beiträge
 
#6

Re: Interfaces werden nicht dereferenziert

  Alt 1. Jul 2005, 14:52
Ich füge zu der Liste noch das klassische Problem von Referenzzählung hinzu:
Dein Problem ist, dass eine Interface instanz eine Interface instanz besitzt, die eine Referenz auf die Container instanz hält, right?
Mit RefCounting lässt sich dieser Zustand nicht elegant lösen.
Beiden halten somit mindestens ein Referenz (sich gegenseitig) obwohl sie vielleicht schon beide vom Programm aus nicht mehr referenziert werden können.

Eine billige Lösung wäre TAggregatedObject als Basisklasse für das untergeordnete Element.
Dabei werden AddRef/Release zum Controller durchgeschliffen. Aber auch das ist nur ein dummer Hack.
Ein Release auf den Controller wird kein Release des Aggregatfeldes auslösen und somit ist nicht sichergestellt, dass es mit freigegeben wird.
Außerdem gibt es keinerlei Garantie dass es immer geht, denn du wirst oft feststellen dass in solchen Situationen...
Delphi-Quellcode:
begin
  with ControllerFactory.GetController do
    GetDingsBums.DoSomething();
end;
... weder Controller noch Dingsbums freigegeben werden _könnten_.

Vielleicht bin ich als .Net Entwickler zu verwöhnt um mir wegen solchen dummen Kleinigkeiten wie RefCounting die Laune vermiesen zu lassen.
Aber wenn das ein neues Projekt wird, dann nehme .Net solange es dir noch möglich ist.
Diese Spielereien mit Interfaces unm explizites Freigeben zu verhindern sind ja ganz nett (selbst oft genutzt..). Aber IMHO viel zu nervig als eine Garbage Collection, die kein RefCounting braucht...
  Mit Zitat antworten Zitat
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#7

Re: Interfaces werden nicht dereferenziert

  Alt 1. Jul 2005, 14:53
Zitat von xaromz:
Muss bei *** ein I := nil; stehen oder erkennt Delphi hier die Zuweisung auf eine bereits "gefüllte" Variable und gibt das vorherige Interface frei? Ich vermute mal nein, oder?
Doch, jede Zuweisung an einen Interface-Pointer löst im Hintergrund einen Aufruf an _Release aus.
Es spielt keine Rolle, ob bei der Zuweisung nil oder ein neuer Wert gesetzt wird.
(also braucht du die Zeile mit *** nicht)
Delphi-Quellcode:
// Pseudocode
procedure InterfacePointerZuweisung(var oldptr, newptr:Pointer);
begin
   if oldptr=newptr then // keine Ahnung, ob Delphi dies beachtet; ich würd's so machen
      Exit;

   if oldptr <> nil then
      IUnknown(oldptr)._Release;
   oldptr := newptr;
end;
Andreas
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#8

Re: Interfaces werden nicht dereferenziert

  Alt 1. Jul 2005, 15:27
Hallo,

Zitat von Robert_G:
Aber wenn das ein neues Projekt wird, dann nehme .Net solange es dir noch möglich ist.
Diese Spielereien mit Interfaces unm explizites Freigeben zu verhindern sind ja ganz nett (selbst oft genutzt..). Aber IMHO viel zu nervig als eine Garbage Collection, die kein RefCounting braucht...
mein Projekt hat wie erwähnt schon > 130.000 loc, ist also nicht mehr ganz neu .
Das automatische Freigeben ist ein Feature, das ich eigentlich gar nicht brauche (das im Gegenteil eher lästig ist, daher ja dieser Thread). Explizites Freigeben hat bisher gut funktioniert, ist jetzt aber natürlich nicht mehr möglich.

Gruß
xaromz
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:41 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