Archief - [C#] Verminderen van geheugenverbruik

Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.

VenomGameworld

Legacy Member
Hey,

Op de moment ben ik aan het werken aan een programma dat één of meerdere partities monitort en de verhouding gebruikte ruimte/vrije ruimte grafisch weergeeft. Dit laat ik om de x aantal minuten updaten.

De structuur is als volgt: ik heb een klasse HDmonitor die gebaseerd is op moederklasse Control. Hierin heb ik 2 verschillende threads: 1 "gewone" waarin de lay-out o.a. wordt getekend en 1 welke ik gebruik voor het updaten.

Alles werkt wel, alleen blijft het geheugenverbruik in de taskmanager maar stijgen. Ik laat bepaalde objecten weliswaar disposen, maar blijkbaar is dit niet genoeg. Ook heb ik al wat zitten knoeien met de Garbage Collector maar dit geeft hetzelfde resultaat.

Dit is de code van de update-thread:

Code:
private void GetData()
        {

            Thread.Sleep(1500);

            int balk, teller;
            double omzetten;
            DriveInfo drive = new DriveInfo(letter);

            teller = 0;

            while (doorgaan)
            {

                if (drive.IsReady)
                {
                    vrij = (int)(drive.TotalFreeSpace / 1073741824);
                    totaal = (int)(drive.TotalSize / 1073741824);

                    omzetten = Math.Round(((double)(totaal) / (double)(200)), 2);
                    balk = (int)(vrij / omzetten);
                    gebruik.Width = balk;
                }

                balk = 0;
                omzetten = 0;

                teller++;

                if (teller == 2)
                {
                    GC.Collect();
                    teller = 0;
                }

                this.BeginInvoke(handler);

                Thread.Sleep(10000);

            }

        }

Weet er iemand hoe ik dit kan oplossen?
Bij voorbaat dank!

WHiSPy

Legacy Member
Misschien even illustratie maken: manueel de GC laten disposen gaat niet altijd dat event triggeren. Het is echter 'n advies naar je runtime toe dat ie zou kunnen gaan garbage collecten. :)

Heb je al de nodige code analysis tools laten lopen op je programma? Die kunnen je misschien nog de nodige adviezen geven.

Krueger

Legacy Member
Ik denk niet dat er iets in die code is die kan leiden tot een constant stijgend geheugenverbruik, ik denk dat je best de rest ook eens post.

VenomGameworld

Legacy Member
@ WHiSPy: ik heb zo'n tooltje (FxCop) laten lopen, maar tussen de resultaten vond ik niets dat het geheugenverbruik zou kunnen verklaren.

@ Krueger: mss was mijn woordkeuze slecht gekozen, maar het geheugenverbruik stijgt niet constant, maar slechts wanneer de update-thread wordt uitgevoerd. dus tijdens het slapen ervan blijft het constant. Meestal stijgt het met een waarde van ongeveer 4 kB. ik zal hieronder de rest van de code posten.

In de bovenstaande code invoke ik dus een bepaalde delegate die een repaint() aanroept. Misschien dat het geheugenverbruik hieraan te wijten is?

Bovenaan in de klasse staat gedeclareerd:
Code:
private refreshHandler handler;

In de constructor wordt hij geïnitialiseerd:
Code:
        public HDmonitor(string station)
        {
            this.letter = station;

            handler = new refreshHandler(refreshFunctie);

            t1 = new Thread(new ThreadStart(GetData));
            t1.Start();

            doorgaan = true;
        }

Code:
private void refreshFunctie()
        {
            this.Refresh();
        }

En dit is de paint-functie zelf:
Code:
        protected override void OnPaint(PaintEventArgs e)
        {

                // Declaraties
                Color kleur = Color.FromArgb(73, 73, 73);
                SolidBrush brush = new SolidBrush(kleur);
                Bitmap afbeelding = new Bitmap(pad);
                Graphics g = e.Graphics;

                // Tekenen van kader
                System.Drawing.Extended.ExtendedGraphics kader = new System.Drawing.Extended.ExtendedGraphics(g);
                kader.FillRoundRectangle(brush, 15.5F, 2.5F, 220.5F, 50.5F, 10.5F);

                // Tekenen van balk
                kleur = Color.FromArgb(118, 118, 118);
                brush = new SolidBrush(kleur);
                g.FillRectangle(brush, 25.5F, 35.5F, 195.0F, 5.5F);

                // Vullen van balk
                g.FillRectangle(new SolidBrush(Color.FromArgb(123, 188, 235)), gebruik);

                // Tekenen van pictogram
                g.DrawImage(afbeelding, 2, 4);

                // Tekenen van tekst
                g.DrawString(vrij.ToString() + "G vrij van " + totaal.ToString() + "G - " + (gebruik.Width / 2) + "%",
                    new Font("Microsoft Sans Serif", 9), new SolidBrush(Color.White), 60.0F, 20.0F);

                g.DrawString(letter.ToUpper() + ":\\",
                    new Font("Microsoft Sans Serif", 9, FontStyle.Bold), new SolidBrush(Color.White), 60.0F, 6.0F);

                // Opruimen
                kader.Graphics.Dispose();
                brush.Dispose();
                afbeelding.Dispose();
                g.Dispose();

        }

Krueger

Legacy Member
Ik denk idd dat het aan je paint functie zal liggen.
De methode die je daar gebruikt om objecten te gebruiken en vrij te geven, is niet echt de standaard manier van werken.

Normaal gebruik je een volgende soort constructie:
Code:
using (SolidBrush brush = new SolidBrush(kleur))
{
      System.Drawing.Extended.ExtendedGraphics kader = new System.Drawing.Extended.ExtendedGraphics(g);
      kader.FillRoundRectangle(brush, 15.5F, 2.5F, 220.5F, 50.5F, 10.5F);
}

Die roept dan zelf de dispose op als het nodig is, in dit geval als hij dus out of scope gaat.
Dit zou normaal het verschil niet mogen maken, aangezien je zelf disposed op het einde, maar het is wel wat netter zo.

Wat ik wel zie is dat je telkens een new doet van je font, hier gebruik je best ook bovenstaand patroon, mss kan je eens kijken of dat al iets oplost.

VenomGameworld

Legacy Member
Ik heb nu het een en ander veranderd aan de paint functie:
Code:
        protected override void OnPaint(PaintEventArgs e)
        {
                // Declaraties
                Color kleur = Color.FromArgb(73, 73, 73);
                Graphics g = e.Graphics;

                // Tekenen van kader
                using (brush = new SolidBrush(kleur))
                {
                    System.Drawing.Extended.ExtendedGraphics kader = new System.Drawing.Extended.ExtendedGraphics(g);
                    kader.FillRoundRectangle(brush, 15.5F, 2.5F, 220.5F, 50.5F, 10.5F);
                }


                // Tekenen van balk
                kleur = Color.FromArgb(118, 118, 118);

                using (brush = new SolidBrush(kleur))
                {
                    g.FillRectangle(brush, 25.5F, 35.5F, 195.0F, 5.5F);
                }


                // Vullen van balk
                using (SolidBrush bar = new SolidBrush(Color.FromArgb(123, 188, 235)))
                {
                    g.FillRectangle(bar, gebruik);
                }


                // Tekenen van pictogram
                g.DrawImage(afbeelding, 2, 4);

                // Tekenen van tekst
                using (Font sansSerif = new Font("Microsoft Sans Serif", 9))
                {
                    g.DrawString(vrij.ToString() + "G vrij van " + totaal.ToString() + "G - " + (gebruik.Width / 2) + "%",
                    sansSerif, wit, 60.0F, 20.0F);

                    g.DrawString(letter.ToUpper() + ":\\",
                    sansSerif, wit, 60.0F, 6.0F);
                }


                // Opruimen
                g.Dispose();

        }


Maar het resultaat is nog altijd hetzelfde. Ik heb nu wel een aantal variabelen helemaal bovenaan gedeclareerd en in de constructor geïnitialiseerd.



Code:
    public class HDmonitor : Control
    {
        private string pad = Application.StartupPath + "\\icon.gif";
        private Rectangle gebruik = new Rectangle(20, 36, 150, 5);
        private int vrij, totaal;
        private Thread t1;
        private delegate void refreshHandler();
        private refreshHandler handler;
        private Boolean doorgaan;
        private string letter;

        private SolidBrush brush, wit;
        private Bitmap afbeelding;


        public HDmonitor(string station)
        {
            this.letter = station;


            afbeelding = new Bitmap(pad);
            wit = new SolidBrush(Color.White);


            handler = new refreshHandler(refreshFunctie);

            t1 = new Thread(new ThreadStart(GetData));
            t1.Start();

            doorgaan = true;
        }

Krueger

Legacy Member
Ik zie het ook niet echt eerlijk gezegd. Wat je wel nog kan proberen is stapsgewijs te werk gaan. Stuk voor stuk code verwijderen, tot het probleem opgelost is. Zo kan je mss achterhalen waar het probleem zit. En als je het zo vindt, mag je altijd eens laten weten wat het juist was, want ik ben wel geïnteresseerd :)

CyBeRRaT

Legacy Member
een klein vraagje, waarom dispose je g ? g wordt aangeleverd door de caller van de paint methode. logischerwijs zal deze dan ook worden opgeruimd door de caller.

probeer eens de profiler los te laten op uw programma. deze zal een veel duidelijker beeld kunnen geven van het geheugengebruik. Hou er ook rekening mee dat de garbage collector manueel laten collecten niet altijd het gewenste resultaat geeft. het kan oa performance serieus verminderen als je dit op een foute plek doet.

je hebt ook nooit vermeld welke proporties dit stijgend gebruik aanneemt. spreek je over mb's, 10tallen mb's, ...

het probleem kan hem liggen in het niet volledig opruimen van de aangemaakte treads. aangeraden wordt om met Backgroundworker te werken.

EDIT: kan je misschien je volledige code posten (als dit mogelijk is uiteraard?) dan kunnen we een veel beter beeld vormen.

WHiSPy

Legacy Member
Mooi dat je alle vragen die al gesteld zijn herhaalt, dus het kan al eens handig zijn om alle posts eens te lezen. ;)

Albireo

Legacy Member
't is de Refresh()

Zelfs bij dit stukje code blijft de Private Working Set gestaag stijgen (8Kb elke 2 of 3 seconden):
Code:
    public partial class Form1 : Form {
        public Form1() {
            myTimer = new Timer();
            myTimer.Interval = 1000;
            myTimer.Tick += new EventHandler(myTimer_Tick);
            myTimer.Start();
        }

        void myTimer_Tick(object sender, EventArgs e) {
            this.Refresh();
        }
        protected override void OnPaint(PaintEventArgs e) {
            base.OnPaint(e);
        }
    }

Windows of .NET zal die resources wel opruimen als 't echt nodig is zeker... :unsure: Zelf kan je er, denk ik, weinig aan doen.

VenomGameworld

Legacy Member
En is er dan geen manier om bepaalde grafische elementen te updaten zonder de Refresh() aan te roepen? Enige workaround ofzo?

Albireo

Legacy Member
'k Heb een hele poos laten draaien met een Refresh elke 25ms en na een tijdje plafon(n)eert het geheugenverbruik. No worries :cool:

VenomGameworld

Legacy Member
Ok, bedankt!
Heeft het dan eigenlijk nog enig nut de verwijzingen naar de Garbage Collector erin te laten zitten? of kan ik deze best verwijderen?

killgore

Legacy Member
imho: best verwijderen, laat de gc automatisch werken, handmatige oproepen zorgen voor meer last als iets anders.

Moto

Legacy Member
brrr nederlandse code...

anyway, in .Net wordt er nogal gemakkelijk geheugen gebruikt als het vrij is, wat ge ook eens kunt doen is de app minimizen en dan eens kijken naar het gebruikte geheugen

H@voc_!nc.

Legacy Member
kader.Graphics.Dispose();
brush.Dispose();
afbeelding.Dispose();
g.Dispose();

da gaat volgens mij allemaal voor geenne meter marsjeren nu nie om de klootzak uit te hangen maar ben gewoon te tam om is uit te proberen.
using statement is inderdaad beter omdat da dan in ne try finally block omvat word en steeds zal uitgevoerd worden no matter what.

en kader.dispose? en multithreading is evil :p dan krijgde zo'n prullen allemaal... en nog meer van da leuks :)

maar het zit allemaal nogal ver weg
imho: best verwijderen, laat de gc automatisch werken, handmatige oproepen zorgen voor meer last als iets anders.
inderdaad blijf daar met uw tikkels af als ge nie weet wa ge aant doen zijn gij kunt gij ne collect zo dikwijls aanroepen als gij wilt zolang ge ne reference hebt dan dan wordt er sjust niks vrij gemaakt...
Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.
Terug
Bovenaan