为什么我的C#控制台应用程序在复制文件时会因为 '内存不足' 异常而崩溃?

3
我写了一个非常简单的程序可以在外部硬盘上找到图片,并将它们放入文件夹中。听起来非常简单,但由于某种原因,在执行此操作时会出现“内存不足”异常。
我已经在装有 4 GB RAM 的 64 位 Win10 和 32 GB RAM 的 64 位 Win10 上进行了测试,但仍然在两个系统上都遇到了“内存不足”的异常。
我的平台目标是 x64。
以下是错误发生的代码:
string[] filePaths = Directory.GetFiles(Stien, "*.*", SearchOption.AllDirectories);

        foreach (string file in filePaths)
        {
            string[] TempValue1 = file.Split(new[] { @"\" }, StringSplitOptions.None);
            string FileName = TempValue1[TempValue1.Length - 1];

            if (FileName.Contains(SøgeTerm)) //Checks if the name contains the search-term.
            {
                if (!SortDate) //If the program was told not to sort by date.
                {
                    try
                    {
                        File.Copy(file, destination + @"\" + FileName, true);
                        Console.WriteLine(FileName + " => " + destination + @"\" + FileName);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("Fejl: " + e.ToString());
                    }
                }
                else
                {
                    Image Billede = Bitmap.FromFile(file);
                    string date;
                    try
                    {
                        PropertyItem propItem = Billede.GetPropertyItem(36867);
                        date = r.Replace(Encoding.UTF8.GetString(propItem.Value), "-", 2);
                        date = date.Split(new[] { ' ' }, StringSplitOptions.None)[0];
                        propItem = null;
                    }
                    catch
                    {
                        date = "UKENDT";
                    }
                    //


                    if (!Directory.Exists(destination + @"\" + date))
                    {
                        Directory.CreateDirectory(destination + @"\" + date);
                        Console.WriteLine(destination + @"\" + date);
                    }

                    File.Copy(file, destination + @"\" + date + @"\" + FileName, true); //Copies the file, and places it into the fitting folder.
                    Console.WriteLine(FileName + " => " + destination + @"\" + "" + date + @"\" + FileName);
                    date = null; //I thought this might helped clearing some memory.
                    Billede = null;
                }
            }

        }

所以我的问题是: 异常是由什么原因引起的,我该如何修复呢?

异常在哪一行发生的? - niceman
检查异常的堆栈跟踪以找到它发生的行。我猜测问题出在Bitmap.FromFile上,可能是因为您的磁盘上有一些像素尺寸较大的图像文件。 - Ralf Bönning
你在打开文件后会关闭它们吗? - Nikhil Agrawal
损坏/无效的图像可以抛出该异常,可能是在 Bitmap.FromFile() 中,您没有筛选以检查文件实际上是否为图像。 - Alex K.
除了 Dispose,你可以在 WriteLine 中使用格式化字符串来代替字符串拼接(如果你使用的是 C# 6,则最好使用字符串插值)。 - niceman
显示剩余3条评论
3个回答

5

你不仅是在复制文件,而且还在将位图加载到内存中,所以最终会导致内存耗尽。

Image Billede = Bitmap.FromFile(file);

仅仅将值设置为null是不够的,你需要处理对象。

因此,不要使用

Billede = null;

Billede.Dispose();

编辑:这样你就可以在代码中做最少的更改。然而,使用USING语句是一种更好的实践(其他答案提供了例子)。

PS-有办法加载图像元数据,而不必将整个图像加载到内存中。

PPS-阅读这篇文章以了解一种在不加载整个图像到内存中的情况下加载拍摄日期信息的方法。它还可以使您的代码运行得更快:如何在Vista上运行C#时找出图片实际拍摄时间?


2
一个using语句是更好的实践,因为它即使在抛出异常或有早期的return语句时也会处理。 - Tim Rogers
@TimRogers,你不能说这是“更好的做法”,因为一切都取决于情况。我见过一些情况,直接使用Dispose比将其包装到using语句中更好。 - mrogal.ski
@m.rogalski 这不是那种情况。 - Tim Rogers
我同意Tim的观点,如果这有所帮助 :) - Mihai Ovidiu Drăgoi

2

ImageBitmap实现了IDisposable接口。因此,您必须将它们的使用包装在using语句中以释放它们的资源。

 using (Image Billede = Bitmap.FromFile(file)){
     ...
 }

尽管这些图像最终会被完成并发布,但在此之前,您可能已经耗尽了内存。

1

正如其他答案所指出的那样,您可以使用using语句以正确的方式调用Dispose方法来处理对象。

using (Image Billede = Bitmap.FromFile(file)){
     ...
 }

然而,对于你的代码,你可能需要检查图像格式,因为Image.FromFile函数会抛出一个OutOfMemoryException异常(来自MSDN):

文件没有有效的图像格式。

-或-

GDI+不支持文件的像素格式。

请查看this


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