You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Framework 4.8下UserControl设置DoubleBuffered=true引发内存泄漏

问题描述

项目基于.NET Framework 4.8,我开发了一个设置DoubleBuffered = true的UserControl。每次调用窗体的Invalidate()方法使该控件失效时,PrivateMemorySize64会增加约1000字节。Paint事件处理程序已被注释,排除了绘制代码导致内存泄漏的可能。测试程序运行14小时后会因OutOfMemoryException崩溃,我原本预期PrivateMemorySize64会保持稳定。

代码示例

UserControl 代码

using System.Drawing;
using System.Windows.Forms;

public partial class BitmapRender : UserControl
{
    private Bitmap TestBmp = new Bitmap(@"D:\test.bmp");

    public BitmapRender()
    {
        InitializeComponent();
        DoubleBuffered = true; // 该行导致PrivateMemorySize64持续增长

        // this.Paint += new System.Windows.Forms.PaintEventHandler(this.This_Paint);            
    }

    /*
    private void This_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    {
        e.Graphics.DrawImage(TestBmp, 0, 0, this.Width, this.Height);          
    }
    */
}

主窗体代码

using System;
using System.Windows.Forms;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        // 每200毫秒执行一次
        BitmapRender1.Invalidate();// 当UserControl设置DoubleBuffered=true时,此调用引发内存泄漏
    }
}
问题分析与解决

原因

.NET Framework 4.8中,启用默认DoubleBuffered的控件,即使没有实际绘制操作,每次调用Invalidate()后,内部的双缓冲机制依然会创建临时缓冲位图,但这些资源未被及时回收,导致内存持续累积。

解决办法

  1. 手动管理双缓冲,替代默认属性
    放弃使用控件自带的DoubleBuffered属性,在需要绘制时手动创建和销毁缓冲位图,确保资源及时释放。修改后的UserControl代码如下:

    using System.Drawing;
    using System.Windows.Forms;
    
    public partial class BitmapRender : UserControl
    {
        private Bitmap TestBmp = new Bitmap(@"D:\test.bmp");
    
        public BitmapRender()
        {
            InitializeComponent();
            // 移除默认的DoubleBuffered设置
        }
    
        protected override void OnPaint(PaintEventArgs e)
        {
            // 使用using语句自动释放缓冲资源
            using (Bitmap bufferBmp = new Bitmap(this.Width, this.Height))
            using (Graphics bufferGfx = Graphics.FromImage(bufferBmp))
            {
                // 执行绘制逻辑(如果需要)
                bufferGfx.DrawImage(TestBmp, 0, 0, this.Width, this.Height);
                // 将缓冲内容绘制到控件
                e.Graphics.DrawImage(bufferBmp, 0, 0);
            }
        }
    }
    
  2. 避免无意义的Invalidate调用
    如果控件没有实际界面更新需求,不要通过定时器频繁调用Invalidate()。仅当控件内容确实需要刷新时,再触发该方法。

  3. 重写OnPaintBackground方法
    重写控件的OnPaintBackground方法并留空,避免默认背景绘制操作创建额外缓冲资源:

    protected override void OnPaintBackground(PaintEventArgs e)
    {
        // 空实现,跳过默认背景绘制
    }
    
  4. 临时缓解方案(不推荐)
    如果必须保留默认双缓冲,可在每次Invalidate()后触发垃圾回收,但这会影响程序性能,仅作为临时应急手段:

    private void timer1_Tick(object sender, EventArgs e)
    {
        BitmapRender1.Invalidate();
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
    

内容的提问来源于stack exchange,提问作者Bob Aune

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 00:20:00