为什么我会出现内存异常,应该如何避免呢?

时间:2014-10-01 06:30:30

标签: c# .net winforms

在我的程序中,我每秒使用计时器捕获一个destop窗口的屏幕截图: 异常消息是:

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: 

例外是在线:

bmpScreenshot.Save(filename, ImageFormat.Gif);

对硬盘来说,图像直到文件编号000110是很好的,这意味着程序工作正常110秒后出现异常。

也许我应该在某处处理bmp变量?

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);
        }
    }
}

1 个答案:

答案 0 :(得分:3)

保存图片后,您需要Dispose它。否则,您将所有责任推到GC。 GC有时可以保存您,有时不会。

鉴于图像大小等于屏幕大小,这意味着图像占用的内存也将是巨大的。你应该在你不需要的时候立即处理它。

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);
    }
}

首选using statementsDispose一次性资源。