在这个相对简单的程序中获得“内存不足”异常

时间:2010-02-05 14:40:19

标签: c# .net image io out-of-memory

这是我的Picture.cs类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;

namespace SharpLibrary_MediaManager
{
    public class Picture:BaseFile
    {
        public int Height { get; set; }
        public int Width { get; set; }
        public Image Thumbnail { get; set; }

        /// <summary>
        /// Sets file information of an image from a given image in the file path.
        /// </summary>
        /// <param name="filePath">File path of the image.</param>
        public override void  getFileInformation(string filePath)
        {
            FileInfo fileInformation = new FileInfo(filePath);
            if (fileInformation.Exists)
            {
                Name = fileInformation.Name;
                FileType = fileInformation.Extension;
                Size = fileInformation.Length;
                CreationDate = fileInformation.CreationTime;
                ModificationDate = fileInformation.LastWriteTime;
                Height = calculatePictureHeight(filePath);
                Width = calculatePictureWidth(filePath);                
            }
        }

        public override void getThumbnail(string filePath)
        {            
            Image image = Image.FromFile(filePath);
            Thumbnail = image.GetThumbnailImage(40, 40, null, new IntPtr());            
        }

        private int calculatePictureHeight(string filePath)
        {
            var image = Image.FromFile(filePath);
            return image.Height;
        }

        private int calculatePictureWidth(string filePath)
        {
            var image = Image.FromFile(filePath);
            return image.Width;
        }
    }
}

在这里,我正在使用该类从给定文件夹中的每个文件中提取信息:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

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

        string folderPath = @"D:\Images\PictureFolder";

        private void button1_Click(object sender, EventArgs e)
        {
            DirectoryInfo folder = new DirectoryInfo(folderPath);
            List<Picture> lol = new List<Picture>();
            foreach (FileInfo x in folder.GetFiles())
            {
                Picture picture = new Picture();
                picture.getFileInformation(x.FullName);
                lol.Add(picture);
            }

            MessageBox.Show(lol[0].Name);
        }
    }
}

我遇到了Out Of Memory异常,我真的不知道为什么。这是我第一次做这样的事情,所以我对批量文件处理等很陌生。

任何帮助人? :)

修改 我打开任务管理器以查看内存使用情况,当我按下按钮运行方法时,我注意到我的内存使用量每秒增加100mb。

编辑2: 在我的文件夹中,我有大约103张图像,每张图像大约100kb。 我需要一个解决方案,无论文件夹中有多少图像都无关紧要。有人建议打开图像,做我的魔法,然后关闭它。我真的不明白'close'的含义。

有人可以推荐一种不同的方法吗? :)

编辑3: 仍然得到内存不足的异常,我根据建议更改了Picture.cs中的代码,但我没有想法。有什么帮助吗?

public override void  getFileInformation(string filePath)
        {
            FileInfo fileInformation = new FileInfo(filePath);

            using (var image = Image.FromFile(filePath))
            {
                if (fileInformation.Exists)
                {
                    Name = fileInformation.Name;
                    FileType = fileInformation.Extension;
                    Size = fileInformation.Length;
                    CreationDate = fileInformation.CreationTime;
                    ModificationDate = fileInformation.LastWriteTime;
                    Height = image.Height;
                    Width = image.Width;
                }
            }
        }

另外,我现在应该开一个新问题吗?这个问题已经有所增长了吗?

11 个答案:

答案 0 :(得分:14)

您没有在Image实例上调用Dispose。还要创建一次图像,然后提取数据。

另见:

http://msdn.microsoft.com/en-us/library/8th8381z.aspx

修改 如果复制了您的代码并使用我的图片库进行了测试。我的平均值FileSize是每个文件2-3 MB。我已经执行了你的程序,它完全应该做到了。 GC完全符合我的预期。

您的程序内存总是大约11-35 MB私人工作集,提交大小稳定在43 MB。

我在1156个文件后中止了程序,总图片大小为2.9 GB。

因此,您必须有其他原因导致内存不足。

这是我的程序输出和代码:

1133: Total Size = 2.842,11 MB
1134: Total Size = 2.844,88 MB
1135: Total Size = 2.847,56 MB
1136: Total Size = 2.850,21 MB
1137: Total Size = 2.853,09 MB
1138: Total Size = 2.855,86 MB
1139: Total Size = 2.858,59 MB
1140: Total Size = 2.861,26 MB
1141: Total Size = 2.863,65 MB
1142: Total Size = 2.866,15 MB
1143: Total Size = 2.868,52 MB
1144: Total Size = 2.870,93 MB
1145: Total Size = 2.873,64 MB
1146: Total Size = 2.876,15 MB
1147: Total Size = 2.878,84 MB
1148: Total Size = 2.881,92 MB
1149: Total Size = 2.885,02 MB
1150: Total Size = 2.887,78 MB
1151: Total Size = 2.890,57 MB
1152: Total Size = 2.893,55 MB
1153: Total Size = 2.896,32 MB
1154: Total Size = 2.898,92 MB
1155: Total Size = 2.901,48 MB
1156: Total Size = 2.904,02 MB

源码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;

namespace SharpLibrary_MediaManager
{
    public abstract class BaseFile
    {
        public string Name { get; set; }
        public string FileType { get; set; }
        public long Size { get; set; }
        public DateTime CreationDate { get; set; }
        public DateTime ModificationDate { get; set; }

        public abstract void getFileInformation(string filePath);

    }


    public class Picture : BaseFile
    {
        public int Height { get; set; }
        public int Width { get; set; }
        public Image Thumbnail { get; set; }

        public override void getFileInformation(string filePath)
        {
            FileInfo fileInformation = new FileInfo(filePath);

            using (var image = Image.FromFile(filePath))
            {
                if (fileInformation.Exists)
                {
                    Name = fileInformation.Name;
                    FileType = fileInformation.Extension;
                    Size = fileInformation.Length;
                    CreationDate = fileInformation.CreationTime;
                    ModificationDate = fileInformation.LastWriteTime;
                    Height = image.Height;
                    Width = image.Width;
                    Thumbnail = image.GetThumbnailImage(40, 40, null, new IntPtr());
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string folderPath = @"C:\Users\arthur\Pictures";

            DirectoryInfo folder = new DirectoryInfo(folderPath);
            List<Picture> lol = new List<Picture>();
            double totalFileSize = 0;
            int counter = 0;
            foreach (FileInfo x in folder.GetFiles("*.jpg", SearchOption.AllDirectories))
            {
                Picture p = new Picture();
                p.getFileInformation(x.FullName);
                lol.Add(p);
                totalFileSize += p.Size;
                Console.WriteLine("{0}: Total Size = {1:n2} MB", ++counter, totalFileSize / 1048576.0);
            }

            foreach (var p in lol)
            {
                Console.WriteLine("{0}: {1}x{2} px", p.Name, p.Width, p.Height);
            }
        }
    }
}

答案 1 :(得分:6)

您必须释放打开Image对象时使用的资源。

您可以调用Dispose,也可以使用Using语句

创建图像

e.g。

public override void getThumbnail(string filePath)
{
    using (Image image = Image.FromFile(filePath))
    {
        Thumbnail = image.GetThumbnailImage(40, 40, null, new IntPtr());
    }
}

由于您的类包含Image,因此您应该实现IDisposable接口,因此您也可以在using语句中使用它。

答案 2 :(得分:5)

我看到了一些问题。首先,您将随后调用CalculatePictureWidth和CalculatePictureHeight两次加载每个Image。其次,你似乎从来没有用缩略图做任何事情。第三,完成后,您应该在Image实例上调用Dispose。

答案 3 :(得分:3)

Image文件包含大块非托管数据的句柄。必须删除此数据,并通过显式调用Image.Dispose来执行此操作。因为您不在calculatePictureHeight和calculatePictureWidth方法中调用Dispose,所以Image文件保存在内存中,这会导致OOM异常。

[更新]: 也许我更多的背景是有用的,为什么这实际上发生了。毕竟,是不是有GC为我们清理这个烂摊子?是的,GC非常有效。它最终还会清理我们必须处理的物体。

Image类包含一个终结器。即使您忘记调用Dispose,此终结器也将确保清除所有资源。但是,这里发生的是内存不足,会立即触发GC.Collect。在此收集期间,GC会查找所有未引用的图像。但是,由于GC不会立即运行终结器方法,所有对象都将提升为Gen1(它们保存在内存中),因此所有资源(本机内存)都是如此。因此,即使在GC启动后,仍然存在太少的内存,并且会出现OOM异常。

这就是为什么你应该总是处理实现IDisposable的对象。

答案 4 :(得分:2)

您不应将所有图像存储在一个列表中,您应该一次只能处理一张图片,并在每次迭代后处理每张图片。

检查Image.Dispose()方法。

答案 5 :(得分:1)

该文件夹中有多少张照片。我看到你循环遍历所有图片并将它们直接加载到内存中并将它们存储在列表中。这可能是原因。

答案 6 :(得分:1)

你的图片文件有多大?您的目录中有多少文件? 根据这些参数,我可以看到如何获得OutOfMemory。您可能想要重新检查您的方法。不要将所有内容都加载到内存中,而是应该单独加载每张图片,执行操作然后继续(当然,在处理完之前的图片之后)。

答案 7 :(得分:1)

尝试一次查看一次图像。我发现了某些.jpg的原因 - 图像处理库存在某些图像的问题,不知道为什么。但是我们已经从photoshop中保存了一些奇怪的方法导致这种情况。我打赌你会发现它是一个坏苹果。

答案 8 :(得分:0)

你能这样做吗?

public class Picture:BaseFile
{
    public int Height { get; set; }
    public int Width { get; set; }
    public Image Thumbnail { get; set; }

    /// <summary>
    /// Sets file information of an image from a given image in the file path.
    /// </summary>
    /// <param name="filePath">File path of the image.</param>
    public override void  getFileInformation(string filePath)
    {
        FileInfo fileInformation = new FileInfo(filePath);
        if (fileInformation.Exists)
        {
            /*
            Name, FileType, Size, etc
            */
            using (Image image = Image.FromFile(filePath))
            {
                Height = image.Height;
                Width = image.Width;
                Thumbnail = image.GetThumbnailImage(40, 40, new Image.GetThumbnailImageAbort(ThumbnailCallback), default(IntPtr));
            }
        }
    }

    public bool ThumbnailCallback()
    {
        return false;
    }
}

答案 9 :(得分:0)

我认为问题是:

如果使用宽度或高度参数之一为-1调用GetThumbnailImage,它也会抛出OutOfMemoryException。

如果使用Image.FromStream创建一个Image对象(例如一个MemoryStream),然后关闭流(MSDN说清楚不要在对象的生命周期内这样做),那么调用此方法(GetThumbnailImage)对象将抛出OutOfMemoryException。

答案 10 :(得分:-1)

有些人提到在完成图像后调用dispose。但是,这还不够。随着内存使用量的增加,.net框架将为您做些什么。由于您已将每个图片添加到链接列表,因此.NET框架无法处理它,因为您隐式保留了引用。

这意味着在foreach循环期间保留图片类中存储的所有数据。从代码片段我看不出它是什么,它可能包括一个压缩的甚至是未压缩的图像版本,可以解释你的内存爆炸。

[编辑]删除了其他人指出的无关的部分内容;我假设图片类是XNA图片类。