0

I'm trying to use a PictureBox to create an animation in a Windows Forms app (C#) by drawing on the Bitmap using Graphics. Unfortunately, it looks like I am leaking memory somewhere or allocating too much based on this error:

An unhandled exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll
Additional information: Out of memory.

This is the source code. Desired behavior is a popup with a glitchy animation.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AnimationApp
{
    public partial class Form1 : Form
{
    Timer timer = new Timer();
    Form1 form1;
    //Bitmap bmp;
    public Form1()
    {
        InitializeComponent();
        form1 = this;
        form1.TransparencyKey = Color.White;
        FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;

        pictureBox1.Left = 0;
        pictureBox1.Top = 0;
        pictureBox1.Width = Screen.PrimaryScreen.Bounds.Width;
        pictureBox1.Height = Screen.PrimaryScreen.Bounds.Height;
        timer.Enabled = true;
        timer.Interval = 10;
        timer.Tick += timer_tick;

    }
    Random rand = new Random();
    int partsection = 0;
    int waitint = 0;
    int ran1 = 0;
    int ran2 = 0;
    int intensifies = 0;
    public void timer_tick(object e, EventArgs ea)
    {
        Bitmap bmp;
        bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);

        using (Graphics g = Graphics.FromImage(bmp))
        {
            if (partsection == -1)
            {

            }
           
            else if (partsection<300)
            {
                if (waitint == 0)
                {

                    ran1 = rand.Next(0, pictureBox1.Width);
                            ran2 = rand.Next(0, pictureBox1.Width);
                    waitint = 10;
                }
                else
                {
                    waitint--;
                }

                g.FillRectangle(new SolidBrush(Color.Green), (float)ran1, (float)0, 3, pictureBox1.Height);
                g.FillRectangle(new SolidBrush(Color.DarkGreen), (float)ran2, (float)0, 3, pictureBox1.Height);

                partsection++;
            }
            else if (partsection < 1000)
            {
                if (intensifies < 255)
                {
                    intensifies++;

                }
                else
                {

                }
                g.FillRectangle(new SolidBrush(Color.FromArgb(intensifies,Color.Black)), (float)0, (float)0, pictureBox1.Width, pictureBox1.Height);
            }

        }
        pictureBox1.Image = bmp;
     
    }
}
}

Thank you for any help.

9
  • You're leaking SolidBrushes, that's for sure. You need to dispose of them. The thing I'm not sure about is if you'd also need to dispose explicitly of the old pictureBox1.Image image. 100 ticks per seconds is also pretty excessive... Commented Dec 18, 2015 at 3:05
  • What code do I need to change? Sorry, I have only been coding in c# for about half a year. Commented Dec 18, 2015 at 3:15
  • Your SolidBrush should be either in using statements like your Graphics or be declared on a separate line so you can call Dispose on em. Or even better, declare them at class level and reuse the same brushes instead of creating a new one each time. For the pictureBox, you can look at this answer. And you can change your Tick to 33 for about 30FPS, which should be fluid enough and less taxing on the hardware. Not sure about the picturebox though. Commented Dec 18, 2015 at 3:18
  • Ok, so like SolidBrush sb1 = new SolidBrush(); //Use Code sb1.Dispose(); Commented Dec 18, 2015 at 3:24
  • 1
    Change the code to use fixed screen resolution of 3840x2160, and it does throw OutOfMemoryException for me. So apparently it does leak, albeit due to late triggered GC (see Ian answer below). Adding if (pictureBox1.Image != null) pictureBox1.Image.Dispose(); right before pictureBox1.Image = bmp; would probably solve the out of memory issue... Commented Dec 18, 2015 at 5:44

2 Answers 2

2

This happens because some of the resources you use are unmanaged and you do not put it under using block or any alternative way to dispose your unmanaged resource after use. The memory is leaked due to that.

For instance, these lines,

g.FillRectangle(new SolidBrush(Color.Green), (float)ran1, (float)0, 3, pictureBox1.Height);
g.FillRectangle(new SolidBrush(Color.DarkGreen), (float)ran2, (float)0, 3, pictureBox1.Height);

Can be changed to

using (SolidBrush greenBrush = new SolidBrush(Color.Green))
using (SolidBrush darkGreenBrush = new SolidBrush(Color.DarkGreen)) {
    g.FillRectangle(greenBrush , (float)ran1, (float)0, 3, pictureBox1.Height);
    g.FillRectangle(darkGreenBrush, (float)ran2, (float)0, 3, pictureBox1.Height);
}

And similarly, this line

g.FillRectangle(new SolidBrush(Color.FromArgb(intensifies,Color.Black)), (float)0, (float)0, pictureBox1.Width, pictureBox1.Height);

To this

using (SolidBrush blackBrush = new SolidBrush(Color.FromArgb(intensifies, Color.Black)))
    g.FillRectangle(blackBrush, (float)0, (float)0, pictureBox1.Width, pictureBox1.Height);

Another advise is you should check the unmanaged resources on System.Drawing. Since there might be more of the unmanaged resources which you show but are not pointed out - or - you do not put in your question but are actually in the other parts of your codes.

Here is more of unmanaged resources:

Here is another explanation for the possibility of Bitmap causes the leak (due to small-sized managed Bitmap class wrapper, out of memory can be caused because the Garbage Collector is late-triggered), see explanation by Mr. Hans Passant

Sign up to request clarification or add additional context in comments.

Comments

1

Your bitmap needs to be declared as an instance variable of the form.

EDIT

Specifically, uncomment your declaration of the Bitmap in the class instance variables. Next, add the following code:

    protected override void OnResize(EventArgs e)
    {
        pictureBox1.Left = 0;
        pictureBox1.Top = 0;
        pictureBox1.Width = Screen.PrimaryScreen.Bounds.Width;
        pictureBox1.Height = Screen.PrimaryScreen.Bounds.Height;
        bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
        base.OnResize(e);
    }

Why? Because you need to resize your picture box when the form resizes, or the user changes the screen resolution. Now, the Bitmap is only declared a single time. I ran the code for several minutes with no ill effects.

BTW, the pictureBox1 resizing code was removed from the constructor.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.