简体   繁体   中英

Why i'm getting out of memory exception and how should i avoid it?

In my program i'm capturing a screenshot of the destop window each second using a timer: The exception message is:

System.OutOfMemoryException was unhandled
  HResult=-2147024882
  Message=Out of memory.
  Source=System.Drawing
  StackTrace:
       at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
       at System.Drawing.Image.Save(String filename, ImageFormat format)
       at CapturedDesktop.Form1.screenshots(String filename) in d:\C-Sharp\CapturedDesktop\CapturedDesktop\CapturedDesktop\Form1.cs:line 53
       at CapturedDesktop.Form1.CaptureScreenshot() in d:\C-Sharp\CapturedDesktop\CapturedDesktop\CapturedDesktop\Form1.cs:line 31
       at CapturedDesktop.Form1.timer1_Tick(Object sender, EventArgs e) in d:\C-Sharp\CapturedDesktop\CapturedDesktop\CapturedDesktop\Form1.cs:line 63
       at System.Windows.Forms.Timer.OnTick(EventArgs e)
       at System.Windows.Forms.Timer.TimerNativeWindow.WndProc(Message& m)
       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
       at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.Run(Form mainForm)
       at CapturedDesktop.Program.Main() in d:\C-Sharp\CapturedDesktop\CapturedDesktop\CapturedDesktop\Program.cs:line 19
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

The exception is on the line:

bmpScreenshot.Save(filename, ImageFormat.Gif);

It was savig fine to the hard disk the images untill file number 000110 that's mean the program was working fine for 110 seconds and then the exception came up.

Maybe i should dispose the bmp variable somewhere ?

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;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.IO;
using unfreez_wrapper;

namespace CapturedDesktop
{
    public partial class Form1 : Form
    {
        int screens;
        string gifsdirectory;
        UnFreezWrapper unfreez;

        public Form1()
        {
            InitializeComponent();

            unfreez = new UnFreezWrapper();
            timer1.Enabled = false;
            screens = 0;
            gifsdirectory = @"C:\Temp\CapturedDesktop\";
        }


        private void CaptureScreenshot()
        {
            screens++;
            screenshots(gifsdirectory + screens.ToString("D6") + ".gif");
        }

        public static void screenshots(string filename)
        {
            //Create a new bitmap.
            var bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
                                           Screen.PrimaryScreen.Bounds.Height,
                                           PixelFormat.Format32bppArgb);

            // Create a graphics object from the bitmap.
            var gfxScreenshot = Graphics.FromImage(bmpScreenshot);

            // Take the screenshot from the upper left corner to the right bottom corner.
            gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
                                        Screen.PrimaryScreen.Bounds.Y,
                                        0,
                                        0,
                                        Screen.PrimaryScreen.Bounds.Size,
                                        CopyPixelOperation.SourceCopy);

            // Save the screenshot to the specified path that the user has chosen.
            bmpScreenshot.Save(filename, ImageFormat.Gif);
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            CaptureScreenshot();
        }

        private void AnimatedGifButton_Click(object sender, EventArgs e)
        {
            List<string> myGifList = new List<string>();
            FileInfo[] fi;
            DirectoryInfo dir1 = new DirectoryInfo(gifsdirectory);
            fi = dir1.GetFiles("*.gif");
            for (int i = 0; i < fi.Length; i++)
            {
                myGifList.Add(fi[i].FullName);
            }
            unfreez.MakeGIF(myGifList, gifsdirectory + "agif", 100, true);
        }
    }
}

After saving the Image, You need to Dispose it. Otherwise you push all the responsibility to GC. GC can sometimes save you, sometimes not.

Given that Image size is equal to screen size, which means that memory taken by the image will also be huge. You ought to Dispose it as soon as you don't need it.

public static void screenshots(string filename)
{
    //Create a new bitmap.
    using( var bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
                                   Screen.PrimaryScreen.Bounds.Height,
                                   PixelFormat.Format32bppArgb))
    {
        // Create a graphics object from the bitmap.
        using(var gfxScreenshot = Graphics.FromImage(bmpScreenshot))
        {

            // Take the screenshot from the upper left corner to the right bottom corner.
            gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
                                    Screen.PrimaryScreen.Bounds.Y,
                                    0,
                                    0,
                                    Screen.PrimaryScreen.Bounds.Size,
                                    CopyPixelOperation.SourceCopy);
        }
        // Save the screenshot to the specified path that the user has chosen.
        bmpScreenshot.Save(filename, ImageFormat.Gif);
    }
}

Prefer using statements to Dispose a disposable resource.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM