Laden...

Exemplarisch: Provozierter Speicherüberlauf führt zu Fehlverhalten

Erstellt von der Marcel vor 18 Jahren Letzter Beitrag vor 18 Jahren 1.899 Views
der Marcel Themenstarter:in
564 Beiträge seit 2006
vor 18 Jahren
Exemplarisch: Provozierter Speicherüberlauf führt zu Fehlverhalten

Hallo Community!

Das kleine Programm wurde nur zu Testzwecken (mit Ideengabe von herbivore) geschrieben, um die Speicherverwaltung auf die Probe zu stellen. Ich habe damit kein wirkliches Problem, sondern mir ist ein offensichtliches Fehlverhalten ins Auge gesprungen, welches ich hier mal diskutieren wollte. 🙂

Wie gesagt, provoziert das Programm zu Testzwecken einen Speicherüberlauf. Dazu werden konsequent byte-Arrays in den Speicher geschrieben. Damit diese nach dem Erstellen in der Schleife nicht dem Garbage Collector zum Opfer fallen, ist das ganze über eine verkette Liste gelöst. Ich habe eine Klasse ramobject, welche ein byte-Array und eine Referenz zum jeweils nächsten ramobject in der Liste beinhaltet. In einer Schleife werden fleißig Elemente in diese Liste eingefügt und der Ram läuft dabei zu. Der Algorithmus tut ohne Probleme das, was er soll.
Nun zu dem Problem: Ich bin dabei auf die Idee gekommen, die Gesamtgröße der erstellten byte-Arrays in einem Label ausgeben zu lassen. Baue ich diese Codezeile dafür ein (Label.Text-Eigenschaft setzen) bleibt der Ram-Verbrauch plötzlich konstant. Auch das Benutzen von GC.KeepAlive() auf jedem Objekt in der Liste ändert nichts daran (habe es zuerst auf den Garbage Collecotr geschoben). Bis dahin hatte ich diesen Durchlauf im GUI-Thread belassen und für die GUI-Bedienbarkeit Application.DoEvents() jedesmal ausgeführt. Nun wird es richtig merkwürdig: Die ganze Methode dafür habe ich in einen zweiten Thread ausgelagert und verändere das Label mit BeginInvoke(). Prinzipiell bleibt das Problem das selbe (Verbrauch konstant), aber es kommt ein neues dazu: Obwohl das in einem zweiten Thread läuft (und der GUI-Thread nichts zu tun hat), schläft der GUI-Thread ein und lässt sich nur am Leben erhalten, wenn der zweite Thread in der Schleife Application.DoEvents() ausführt (Nein, ich habe mich nicht verschrieben 🙂 ). Als Krönung obendrauf: Das passiert nur manchmal - ab und zu läuft die Nachrichtenverarbeitung weiter und die GUI schläft nicht ein - allerdings bleibt in dem Fall der Ram-Verbrauch konstant.

Der wichtigsten Programmcode:

die Klasse der einzelenen Listenelemente:


public class ramobject //Element-Klasse
    {
        public ramobject()
        {
            buffer = new byte[1024];
        }

        public ramobject(int bufferLength)
        {
            buffer = new byte[bufferLength];
        }
        
        private ramobject a; //das nächste Element
        private byte[] buffer; //das Array um Speicher zu verbrauchen

        public ramobject A
        {
            get { return a; }
            set { a = value; }
        }

        public byte[] Buffer
        {
            get { return buffer; }
            set { buffer = value; }
        }
    }

Beim klicken auf den Button wird ein neuer Thread angeworfen:


private void startButton_Click(object sender, EventArgs e)
        {
            Thread memoryThread = new Thread(new ThreadStart(doMemoryLeak));
            memoryThread.Start(); //gewöhnlicher Thread-Start          
        }

und das ist die arbeitende Methode mit der kritischen Zeile:


private void doMemoryLeak()
        {
            ramobject aFirst = new ramobject(); //Erstes Objekt wird erzeugt
            GC.KeepAlive(aFirst);
            ramobject aCurr = aFirst;
            int bytesTotal = 0;

            do //neue Elemente werden dem Ende der Liste hinzugefügt
            {
                aCurr.A = new ramobject();
                aCurr = aCurr.A;
                GC.KeepAlive(aCurr);
                bytesTotal += aCurr.Buffer.Length;
                BeginInvoke((ThreadStart)delegate() { outputBytes.Text = bytesTotal.ToString(); }); //Diese Zeile ist für den konstanten Ram-Verbrauch verwantwortlich
                Application.DoEvents(); // auch bei asnychroner Ausführung geht sonst bei der GUI nichts
            }
            while (!cancelRequest); //cancelRequest ist ein Flag zum Abbrechen
            cancelRequest = false;
        }

Vielleicht sieht jemand von euch, wo der Wurm drin steckt. Ich bin dran verzweifelt 🙂

der Marcel

:] 😄Der größte Fehler eines modernen Computers sitzt meist davor 😁 :]

S
8.746 Beiträge seit 2005
vor 18 Jahren

Application.DoEvents() hat schonmal nix im Thread zu suchen. Könnte das Problem durchaus verursachen.

der Marcel Themenstarter:in
564 Beiträge seit 2006
vor 18 Jahren

Hallo svenson!

Da gebe ich dir Recht! Darauf gekommen bin ich so: Erst habe nur einen Thread genutzt (GUI-Thread) und die GUI mit Apllication.DoEvents(); am Leben erhalten. Als ich festgestellt habe Fehlfunktionen. hatte ich Application.DoEvents() auskommentiert und die Methode in einem neuen Thread gestartet. Als ich gemerkt habe, dass trotz des zweiten Threads die GUI blockiert, habe ich zum Versuch Application.DoEvents() wieder reingetan, was Effekte gezeigt hat (wie schon beschrieben). Alles sehr merkwürdig...

der Marcel

:] 😄Der größte Fehler eines modernen Computers sitzt meist davor 😁 :]

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo der Marcel,

das spricht aber dafür, dass das Fenster, das blockiert, doch in dem Thread läuft. Ein Fenster läuft immer in dem Thread, der es erzeugt.

herbivore

der Marcel Themenstarter:in
564 Beiträge seit 2006
vor 18 Jahren

Hallo herbivore! 🙂

Ich starte aber einen zweiten bei einem Button-Klick... und trotzdem verhält sich das ganze so. Oder habe ich in meinem Quelltext was falsch? (Methode startButton_Click)

der Marcel

:] 😄Der größte Fehler eines modernen Computers sitzt meist davor 😁 :]

I
1.739 Beiträge seit 2005
vor 18 Jahren

>
ramobject aFirst = new ramobject(); //Erstes Objekt wird erzeugt
GC.KeepAlive(aFirst);
ramobject aCurr = aFirst;
>

>
do //neue Elemente werden dem Ende der Liste hinzugefügt
{
aCurr.A = new ramobject();
aCurr = aCurr.A;
>
Vielleicht ist es ja spät aber:
Ich seh maximal zwei referenzierte Objekte, der Rest sieht wie weggeworfen aus...

Edit: Es ist spät....
Morgen dann also.

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo ikaros,

über aFirst sind aber indirekt alle Objekte in der verketteten Liste referenziert.

herbivore

I
1.739 Beiträge seit 2005
vor 18 Jahren

Stimmt, hab ich aber erst nach dem Post gesehen(war halt schon ein wenig blind, also spät)