如何不使用按钮调用Load_form函数,实现图片框自动更新并解决内存溢出问题
解决方案
异常原因
你遇到的Out of memory和Parameter is not valid异常本质是GDI资源泄漏:
- Image、Bitmap、Graphics这类对象都属于非托管GDI资源,不会被GC自动回收,必须手动调用
Dispose()释放 - 原有代码每次加载新图片时,旧的图片资源没有被释放,累积到系统GDI对象上限就会抛出异常
- 你代码中已经写了
FileSystemWatcher的回调但没有初始化启用,还是用定时器轮询,无意义的重复加载进一步加速了资源消耗
修复步骤
1. 修复资源泄漏
修改setPaths方法,每次加载新图片前先释放所有旧资源,同时及时释放临时创建的原始图片、Bitmap对象,另外用FileStream加载避免锁死源图片文件:
public void setPaths(string[] paths, double defZoom) { // 先释放所有旧的GDI资源 if (pictureBox1.Image != null) { pictureBox1.Image.Dispose(); pictureBox1.Image = null; } if (pictureBox2.Image != null) { pictureBox2.Image.Dispose(); pictureBox2.Image = null; } if (pictureBox3.Image != null) { pictureBox3.Image.Dispose(); pictureBox3.Image = null; } if (actImage != null) { actImage.Dispose(); actImage = null; } if (actImage1 != null) { actImage1.Dispose(); actImage1 = null; } if (actImage2 != null) { actImage2.Dispose(); actImage2 = null; } viewerPaths = paths; pathsNumber = viewerPaths.Length; imageDefaultZoom = defZoom; int total = viewerPaths.Length; // 先判断文件数量足够再加载,避免索引越界 if (pathsNumber >= 3) { using (var fs = new FileStream(viewerPaths[total - 3], FileMode.Open, FileAccess.Read)) using (actImage = Image.FromStream(fs)) using (var b = new Bitmap(actImage)) { var i = resizeImage(b, new Size(100, 100)); pictureBox1.Image = i; } using (var fs = new FileStream(viewerPaths[total - 2], FileMode.Open, FileAccess.Read)) using (actImage1 = Image.FromStream(fs)) using (var b1 = new Bitmap(actImage1)) { var i1 = resizeImage(b1, new Size(100, 100)); pictureBox2.Image = i1; } using (var fs = new FileStream(viewerPaths[total - 1], FileMode.Open, FileAccess.Read)) using (actImage2 = Image.FromStream(fs)) using (var b2 = new Bitmap(actImage2)) { var i2 = resizeImage(b2, new Size(100, 100)); pictureBox3.Image = i2; } } }
2. 启用FileSystemWatcher代替定时器轮询
用系统级的文件监听器实现新增文件实时感知,比定时器轮询效率更高,注意处理跨线程更新UI的问题:
// 全局声明监听器对象 private FileSystemWatcher _watcher; private void Form1_Load(object sender, EventArgs e) { imPaths = getFiles(pathLogNames, actCode, ".tif"); setPaths(imPaths, 0.5); // 初始化文件监听器 _watcher = new FileSystemWatcher(pathLogNames); _watcher.Filter = "*.tif"; // 和你原getFiles的递归扫描逻辑对应,不需要扫子目录就设为false _watcher.IncludeSubdirectories = true; _watcher.Created += OnChanged; _watcher.EnableRaisingEvents = true; } private void OnChanged(object sender, FileSystemEventArgs e) { // FileSystemWatcher的回调在工作线程,必须回到UI线程操作控件 if (this.InvokeRequired) { this.Invoke(new Action(() => OnChanged(sender, e))); return; } // 加100ms延时,避免文件还在写入时读取报错 System.Threading.Thread.Sleep(100); imPaths = getFiles(pathLogNames, actCode, ".tif"); // 判断文件列表真的有变化再更新,减少不必要的资源消耗 if (!imPaths.SequenceEqual(viewerPaths)) { setPaths(imPaths, 0.5); } }
3. 窗体关闭时清理剩余资源
添加窗体关闭事件处理,彻底释放所有资源:
private void Form1_FormClosing(object sender, FormClosingEventArgs e) { _watcher?.Dispose(); if (pictureBox1.Image != null) pictureBox1.Image.Dispose(); if (pictureBox2.Image != null) pictureBox2.Image.Dispose(); if (pictureBox3.Image != null) pictureBox3.Image.Dispose(); }
内容的提问来源于stack exchange,提问作者Oguz Kahraman
相关产品推荐
相关产品推荐

