Laden...

Garbage Collector zerstört Objekt obwohl er nicht soll

Erstellt von thefiloe vor 12 Jahren Letzter Beitrag vor 12 Jahren 2.063 Views
T
thefiloe Themenstarter:in
87 Beiträge seit 2010
vor 12 Jahren
Garbage Collector zerstört Objekt obwohl er nicht soll

Also folgendes:
Ich habe eine Klasse die Audio daten wiedergibt. Und jetzt habe ich gerade eine kleine Test-App geschrieben für die ersten mp3 tests. Und bis dahin hat es auch nie solche Probleme gegeben. Doch wenn ich jetzt die ganze Zeit die Größe des Fensters verändere wird aufeinmal die Klasse zerstört. Ich habe keine Ahnung wieso. Kann man nicht in der Klasse etwas mache, dass der GC. überhaupt nicht mehr das Teil zerstören darf? Oder zumindest solange nicht bis ich Dispose manuell aufrufe.

Hier der Coder der Testapp welcher an sich nix großes ist und aber funktioniert solange ich nich am Fenster rumziehe....

namespace Mp3FrameConverterTest
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofn = new OpenFileDialog();
            ofn.ShowDialog();

            Mp3File file = new Mp3File(ofn.FileName);
            WaveOut waveOut = new WaveOut(file);

            waveOut.Init();
            waveOut.Play();
        }

    }
}
5.657 Beiträge seit 2006
vor 12 Jahren

Hi thefiloe,

wie kommst du darauf, daß der GC dir Objekte zerstört? Hast du da irgendwelche Anhaltspunkte dafür, und wenn ja, warum enthältst du die uns vor?

Christian

Weeks of programming can save you hours of planning

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo thefiloe,

der GC räumt ein Objekt ab, wenn keine Referenz merh darauf besteht. In deinem Code sind Mp3File und WaveOut innerhalb einer Methode deklariert, d.h. nach verlassen der Methode kann der GC diese abräumen. Mach Felder daraus, dann wird die Referenz auch nach dem verlassen der Methode gehalten und der GC räumt es nicht mehr ab.


    public partial class frmMain : Form
    {
        private Mp3File _mp3File;
        private WaveOut _waveOut,

        public frmMain()
        {
            InitializeComponent();
        }

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

T
thefiloe Themenstarter:in
87 Beiträge seit 2010
vor 12 Jahren

ja das dachte ich mir schon, dass er das Teil deshalb wegräumt weil es "nicht mehr benötigt" wird.
Doch das Problem ist ich schreibe eine Bibliothek und da ist es nicht tragbar wenn das Zeug einfach weggeräumt wird. Deshalb kann man nicht innerhalb der Klasse dem GC verbieten sich an der Klasse zu schaffen zu machen.

Außerdem welche Anhaltspunkte ich habe... nun ja ich denke mal es ist die einzige logische Lösung.
Ich fahre das Programm nicht herunter und Zerstöre es nirgends. Von dem her kann es nur durch Zauberhand zerstört werden oder über den GC.

1.552 Beiträge seit 2010
vor 12 Jahren

Hallo thefiloe,

ich denke dein "Ich benötige das Object" noch und das C# "Ich benötige das Object noch" sind 2 verschiedene paar Schuhe.

wenn du folgendes schreibst:


string s;
{
    int i = 5;
    s = i.ToString();
}

Ausserhalb der Klammer sagst C# von sich: Ich brauch i nicht mehr. Wenn du es selbst aber noch benötigst, musst du den scope (also die Sichtbarkeit) wie gfoidl sagte "vergrößern".

Gruß
Michael

Mein Blog
Meine WPF-Druckbibliothek: auf Wordpress, myCSharp

T
thefiloe Themenstarter:in
87 Beiträge seit 2010
vor 12 Jahren

also kann man den GC also nicht dazu anweisen das object stehen zu lassen.
Sprich wie jetzt z.B. in nem Konstruktor dass du sagst GC.Lassesstehen(this);

2.298 Beiträge seit 2010
vor 12 Jahren

Nö aber du kannst den verwender deiner Bibliothek anweisen, Instanzen des Objektes als Klassenmember zu nutzen. Wenn du dies so fest Dokumentierst, ist er selbst schuld, wenn ers nicht macht.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

5.657 Beiträge seit 2006
vor 12 Jahren

Hallo allersiets

die Referenzen müssen doch trotzdem erhalten bleiben, also wenn ich WAVOut die Referenz auf die MP3-Datei übergebe, muß es doch dort intern eine Referenz auf die MP3-Datei geben, damit sie auch abgespielt werden kann. Das würde bedeuten, daß der GC den Speicher nicht freigibt. Daher kann ich die Schlußfolgerung mit dem Feld nicht ganz nachvollziehen.

Von daher wäre mal ein dahingehendes Feedback vom OP ganz nützlich

Christian

Weeks of programming can save you hours of planning

P
157 Beiträge seit 2010
vor 12 Jahren

Aber auf _waveOut wird doch nicht mehr verwiesen oder.

5.742 Beiträge seit 2007
vor 12 Jahren

Deshalb kann man nicht innerhalb der Klasse dem GC verbieten sich an der Klasse zu schaffen zu machen.

Der GC schafft nichts weg, worauf noch eine erreichbare (!) Referenz zeigt.

Insofern:

also wenn ich WAVOut die Referenz auf die MP3-Datei übergebe, muß es doch dort intern eine Referenz auf die MP3-Datei geben,

Diese existiert zwar, aber wenn WAVOut nicht mehr erreichbar ist, besteht auch keine Notwendigkeit mehr, die Datei "am Leben" zu halten; der GC räumt beide auf.

Aber auf _waveOut wird doch nicht mehr verwiesen oder.

Das ist in dem Sinne egal: Man kriegt zwar eine Warnung, die aussagt, dass man das Feld nur zuweist, in diesem Fall kann man diese aber ignorieren (bzw. fixen, indem man IDisposable implementiert, da man WAVOut garantiert disposen muss).
Entscheidend ist nur, dass man noch darauf zugreifen könnte.

Daher kann ich die Schlußfolgerung mit dem Feld nicht ganz nachvollziehen.

Macht man aus waveOut ein Feld (für die Datei braucht man nicht zwangsläufig eins), ist die Referenz auch nach Verlassen der Methode noch erreichbar - folglich räumt der GC nichts weg.

GC.Lassesstehen(this);

Und was sollte das bringen (außer, ein Memoryleak zu erzeugen)?

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo thefiloe,

nur der vollständigkeithalber:

GC.Lassesstehen(this);

Sowas ginge mit GC.KeepAlive Method (System), allerdings ist das hier nicht nötig und sogar schlecht (mehr ein Hack als Code), denn es gibt viel bessere Möglichkeiten. Diese wurden auch in diesem Thread schon (mehrfach) erwähnt.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

5.742 Beiträge seit 2007
vor 12 Jahren

Sowas ginge mit
>

Nein, auch da räumt der GC nach Aufruf der Methode auf.
Im Prinzip ist GC.KeepAlive nichts weiter als eine normale, leere Methode, die - alleine durch die Übergabe des Objekts als Parameter - sicherstellt, dass das Objekt auf jeden Fall bis zum Aufruf der Methode "überlebt".

Kleines Beispiel, wofür man KeepAlive brauchen kann:


public static void Main()
{
   Timer timer = new Timer(Tick, null, 0, 1000);

   //GC.KeepAlive(timer);
   Console.ReadLine();
   //GC.KeepAlive(timer);
}

private static void Tick(object state)
{
   Console.WriteLine("Tick!");

   GC.Collect(); //Kann man theoretisch auch weglassen - macht das Ergebnis nur etwas offensichtlicher
}

Je nachdem, welches der beiden KeepAlive man auskommentiert, verhält sich das Programm anders:
Beim oberen KeepAlive wird nur einmal (oder theoretisch keinmal) "Tick" ausgegeben, beim zweiten jede Sekunde (wie erwartet).

Nach dem Aufruf verändert sich aber absolut gar nichts daran, wie der GC das Objekt behandelt.

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo winSharp93,

das widerspricht ja nicht meiner Antwort. Das was du erklärt hast steht genauso im Link. Und dass der Code hackmäßig zu ändern ist, damit KeepAlive greift hab ich auch erwähnt.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

5.742 Beiträge seit 2007
vor 12 Jahren

das widerspricht ja nicht meiner Antwort.

Doch: thefiloe hat mit dem "GC.Lassesstehen" nach einer Methode gesucht, die ein Objekt auch nach Ausführung der Methode ohne Verwendung eines Feldes am Leben hält (quasi als ob man etwas einfach in einem statischen Feld speichert).
Aber genau das macht KeepAlive eben nicht: Insofern könnte man GC.KeepAlive auch noch so oft in dem ursprünglichen Code aufrufen: Das Problem würde damit immer noch bestehen, es würde alles beim alten bleiben.
KeepAlive hält eben nicht dauerhaft, sondern nur bis zum Aufruf "am Leben".

GC.KeepAlive zu verwenden wäre also kein Hack (von einem solchen kann man m.E. nur sprechen, wenn er auch tatsächlich das gewünschte Ergebnis liefert) und somit auch keine Alternative (auch keine schlechte).

R
103 Beiträge seit 2009
vor 12 Jahren

@winsharp93

Danke für die gute erklärung von GC.KeepAlive() , hatte mich schonmal gewundert warum es nicht so funktioniert wie schon von vielen missverständlich eklärt und von mir immer falsch verstanden...

Es müsste eigentlich GC.KeptAliveUntilHere() heissen. 😉

5.657 Beiträge seit 2006
vor 12 Jahren

@winsharp93

Danke für die gute erklärung von GC.KeepAlive()

Genau, jetzt hab ich's auch verstanden 😃

Also wäre doch die einfachste (oder einzige?) Lösung die Verwendung eines Feldes, was sollte also dagegensprechen, es so zu machen?

Schöne Grüße,
Christian

Weeks of programming can save you hours of planning

5.742 Beiträge seit 2007
vor 12 Jahren

Also wäre doch die einfachste (oder einzige?) Lösung die Verwendung eines Feldes, was sollte also dagegensprechen, es so zu machen?

Ja, genau. Dagegen spricht auch absolut nichts 😉

Man muss nur aufpassen, dass man das Feld nicht versehentlich "wegoptimiert", da es ja scheinbar nie ausgelesen wird - also unbedingt ein Kommentar bzw. ein Attribut zu Dokumentationszwecken anbringen.
Daher auch mein Hinweis auf IDisposable - diese Schnittstelle zu implementieren, macht in diesem konkreten Zusammenhang sicherlich Sinn und nebenbei verwendet man das Feld dann auch tatsächlich für etwas 🙂

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo zusammen,

die reine Existenz einer Referenz ist sowie so nicht ausreichend. Der GC kann auch Objekte, die noch referenziert werden, entfernen, wenn er erkennen kann, dass die Referenz im Code nicht mehr angesprochen wird.

Siehe dazu auch MemoryLeaks / nicht abgehängte Events.

herbivore

5.742 Beiträge seit 2007
vor 12 Jahren

Der GC kann auch Objekte, die noch referenziert werden, entfernen, wenn er erkennen kann, dass die Referenz im Code nicht mehr angesprochen wird.

...wobei bei einer Referenzierung durch eine Instanzvariable das ausgeschlossen sein sollte (vorausgesetzt die Instanz, welche die Variable hält, ist auch entsprechend erreichbar) - sonst könnte man ja per Reflection evtl. Referenzen auf bereits collectete Objekte erhalten.