【问题标题】:Out of memory in image viewer despite using Image.Dispose尽管使用 Image.Dispose,但图像查看器内存不足
【发布时间】:2016-09-16 15:23:47
【问题描述】:

当我在用 C# 编码的图像查看器中添加下一个和上一个导航选项时,当我按 Next 大约 20 次左右时,Visual Studio 告诉我进程内存不足。它在任何有很多文件夹的文件夹中执行此操作,即使它们的图像文件大小都很小

我明白了:

System.Drawing.dll 中发生“System.OutOfMemoryException”类型的未处理异常附加信息:内存不足。

这是我正在使用的代码

private void next_Click(object sender, EventArgs e)
{
    string[] foldernm = Directory.GetFiles(Path.GetDirectoryName(lfoto_file.FileName));
    _pictureIndex++;
    if (_pictureIndex >= foldernm.Length)
    {
        _pictureIndex = 0;
    }
    ibread_img.Image.Dispose();
    ibread_img.Image = Image.FromFile(foldernm[_pictureIndex]);
}

现在你可以看到,我在那里有ibread_img.Image.Dispose();,因为我已经搜索过这个和其他人说要使用那个,但它不起作用,我仍然遇到同样的问题,断点确认代码正在运行,所以我很困惑为什么它仍然内存不足。我正在循环的图像并不大。我已经尝试了所有可以找到的方法,包括将之前加载的图像归零,手动调用垃圾收集器,但似乎没有任何效果。我在 C# 方面不是最擅长的,因此该代码中可能存在可怕的错误或缺陷,但我不知道,关于如何解决此问题的任何想法?

【问题讨论】:

  • 你确定你确实有有效的图像文件吗?
  • 异常抛出后图片还在绘制吗?如果是这样,这可能与以下内容重复:stackoverflow.com/questions/6506089/…
  • 由于您没有指定搜索模式/过滤器,您可能会尝试加载非图像文件。
  • 问题是它不检查下一个文件是否像LarsTech和Plutonix所说的那样,我犯了一个如此愚蠢的错误,我完全忘记并忽略了它,非常感谢很多
  • GDI 有一个坏习惯,就是对各种问题抛出OutOfMemoryException,其中很多根本与内存不足无关。我发现这通常意味着图像文件已损坏,或者是 GDI 无法读取的格式。

标签: c# .net winforms picturebox


【解决方案1】:

您可以采取一些措施来改善您的观看者。首先,您每次都在重新创建图像文件列表;您每次都加载所有这些只是为了访问下一个,并且您不必创建图像来显示它。

// class level vars
int picIndex = 0;
IEnumerable<string> files;
int filesCount;
string picPath;
static string[] imgExts = {".png", ".jpg",".gif"};

由于您提到了“下一个”和“上一个”按钮,因此您必须在其他地方拥有几乎相同的代码。这将消除重复,下一步:

ShowImage(picIndex);

picIndex+=1;
if (picIndex >= filesCount)
    picIndex = 0;

然后是显示所需图像的方法:

private void ShowImage(int Index)
{ 
    // create image list if needed (once)
    if (files == null)
    { 
        files = new DirectoryInfo(picPath).EnumerateFiles().
            Where(q => imgExts.Contains(q.Extension.ToLowerInvariant())).
            Select( z => z.FullName);

        filesCount = files.Count();
    }

    string thisFile = files.ElementAt(Index);

    // no need to dispose an image if you never create one          
    pb2.ImageLocation = thisFile;
    lblImgName.Text = Path.GetFileName(thisFile);
}

每次(在 2 个地方)创建文件列表,它只执行一次,而不是加载所有文件的列表,而是将其保留为 IEnumerable 以根据需要获取它们。它也适用于FileInfo,不区分大小写,主要是为了说明一种不同的方式,如果您愿意,可以按创建日期对它们进行排序(OrderBy)。

最后,给定完整的路径和文件名,您可以使用.ImageLocation 属性并避免创建和处置Images。

主要是尽量减少重复代码的数量,所以你Dont Repeat Yourself。 Next 和 Previous 的代码几乎相同。

【讨论】:

  • 谢谢你,我会试试你说的,我确实想尽可能优化和简化我的代码
【解决方案2】:

感谢 LarsTech 和 Plutonix 指出我的错误。这个新代码现在可以正常工作了:

private void next_Click(object sender, EventArgs e)
    {
        var filteredFiles = Directory.EnumerateFiles(Path.GetDirectoryName(lfoto_file.FileName))
            .Where(file => file.ToLower().EndsWith("jpg") || file.ToLower().EndsWith("png") || file.ToLower().EndsWith("gif") || file.ToLower().EndsWith("bmp") || file.ToLower().EndsWith("tiff") || file.ToLower().EndsWith("ico"))
            .ToList();
        _pictureIndex++;
        if (_pictureIndex >= filteredFiles.Count)
        {
            _pictureIndex = 0;
        }
        ibread_img.Image.Dispose();
        ibread_img.Image = Image.FromFile(filteredFiles[_pictureIndex]);
        init();
    }

我只需要过滤掉正确的格式。

【讨论】:

    猜你喜欢
    • 2015-02-12
    • 1970-01-01
    • 1970-01-01
    • 2023-02-09
    • 1970-01-01
    • 1970-01-01
    • 2019-04-06
    • 1970-01-01
    • 2013-02-21
    相关资源
    最近更新 更多