C#中Bitmap的内存泄漏问题

6

我在应用程序中发现了内存泄漏问题。如果我查看任务管理器,每次启动此进程时,RAM内存增加约300 MB。

Bitmap bmp1 = new Bitmap(2480, 3508);
panel1.DrawToBitmap(bmp1, new Rectangle(0, 0, 2480, 3508));
pictureBox2.Image = bmp1;

有人可以帮我解决这个漏洞吗?如果我使用:

bmp1.Dispose();

我在 "Program.cs" 文件的这一行代码中遇到了一个异常:Application.Run(new Form1()); 然后应用程序停止运行...

屏幕应用程序截图: enter image description here


那个异常是什么?ObjectDisposedException? - Matthew Watson
离题了,但是避免查看任务管理器,而是检查性能监视器。 - V4Vendetta
你需要在使用完图片后将其处理掉。你的应用程序中是否存在不再显示该图片的时刻?如果是,那么就需要在那里将其处理掉。 - juharr
我认为他正在重复这个过程,而旧图像并没有被清除(至少不是立即清除)。 - Adam K Dean
图片始终在picturebox1中的屏幕上。 - Ferry Jongmans
异常信息为:无效参数。 - Ferry Jongmans
4个回答

15

更新:你并没有一个内存泄漏本质上,你只需要等待垃圾回收器释放资源。

如果你确实想让垃圾回收器回收,你可以这样做:

System.GC.Collect();
System.GC.WaitForPendingFinalizers();

你为什么需要处理位图?如果你的PictureBox正在使用它,那么你需要这个位图。如果你要经常更改它,也许你应该将旧位图替换为新位图并处理旧位图:

Bitmap bmp1 = new Bitmap(2480, 3508);
panel1.DrawToBitmap(bmp1, new Rectangle(0, 0, 2480, 3508));
Image img = pictureBox1.Image;
pictureBox1.Image = bmp1;
if (img != null) img.Dispose(); // this may be null on the first iteration

这与调用 Dispose 相同,显然会引发异常。 - juharr
是的,但最好使用“using”表达。 - Adam K Dean
抱歉,我的错,应该是 pictureBox2.Image = (Bitmap)bmp1.Clone();。话虽如此,第二个解决方案可能是最好的,因为我认为第一个解决方案实际上并没有回答你的问题。我已经编辑了答案以使其更有意义。 - Adam K Dean
这个:Bitmap bmp1 = new Bitmap(2480, 3508); panel1.DrawToBitmap(bmp1, new Rectangle(0, 0, 2480, 3508)); Image img = pictureBox1.Image; pictureBox1.Image = bmp1; if (img != null) img.Dispose(); // 第一次它将是null同时增加RAM内存也可以。 - Ferry Jongmans
1
显然,“Bitmap和Image从不关闭底层流。你将永远锁定该文件,直到GC关心收集浮动引用”。因此,您仍然依赖于GC。 - Adam K Dean
显示剩余7条评论

4

我认为你应该只处理掉你不再需要的图片。你仍然需要创建,只是将其设置为pictureBox2.Image字段的内容。可以尝试类似以下的方式:

Bitmap bmp1 = new Bitmap(2480, 3508);
panel1.DrawToBitmap(bmp1, new Rectangle(0, 0, 2480, 3508));
Bitmap bmp2 = (Bitmap)pictureBox2.Image;
pictureBox2.Image = bmp1;
bmp2.Dispose();

免责声明:我对C#不熟悉,因此我的理解可能有误...


嗯,在这一行:Bitmap bmp2 = pictureBox2.Image; 它说:“错误1:无法隐式转换类型'System.Drawing.Image'为'System.Drawing.Bitmap'。存在显式转换(您是否缺少强制转换?)” - Ferry Jongmans
实际上这没有意义,因为只有在picturebox显示Bitmap时,你才需要保持Bitmap的存在。 - V4Vendetta
@V4Vendetta 再看一下代码:这就是我想要它做的。它只处理不再显示的图像...我没有C#,但由于我的答案基本上与Adam K Dean的被接受的答案相同,所以我认为这是有道理的。 - ppeterka
@Ferry,我已经使用显式转换更新了代码,但是稍微搜索一下就可以解决你的问题——那个错误信息并不是很常见的。 - ppeterka

1

为避免位图内存泄漏,您应该使用外部的gdi32.dll。

[System.Runtime.InteropServices.DllImport("gdi32.dll")] 
public static extern bool DeleteObject(IntPtr hObject);
//your bitmap usage code here
...
//delete bitmap handle when you don't need the bitmap anymore
DeleteObject((IntPtr)hBitmap);

-1
    Bitmap copy_Screen()
    {
        try
        {
            var bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
            var gfxScreenshot = Graphics.FromImage(bmpScreenshot);
            try
            {
                gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
            }
            catch (Exception) { }
            return bmpScreenshot;
        }
        catch (Exception) { }
        return new Bitmap(10, 10, PixelFormat.Format32bppArgb);
    }

    private void button5_Click(object sender, EventArgs e)
    {
        //Start Stop timer
        if (timer1.Enabled == false) { timer1.Enabled = true; } else { timer1.Enabled = false; }
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        //memory leak solve
        System.GC.Collect();
        System.GC.WaitForPendingFinalizers();

        Bitmap BM = copy_Screen();
        if (pictureBox1.Image != null)
        {
            pictureBox1.Image.Dispose();
        }
        pictureBox1.Image = BM;

    }

2
你为什么发布这段代码?它解决了什么问题? - Cody Gray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接