Получение исключения «недостаточно памяти» в этой относительно простой программе - PullRequest
6 голосов
/ 05 февраля 2010

Вот мой класс 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);
        }
    }
}

Я получаю исключение "Недостаточно памяти" и не знаю почему. Это первый раз, когда я делаю что-то подобное, поэтому я довольно плохо знаком с пакетной обработкой файлов и т. Д.

Любая помощь, ребята? :)

Edit: Я открыл диспетчер задач, чтобы увидеть использование памяти, и когда я нажимаю кнопку, чтобы запустить метод, я замечаю, что мое использование памяти увеличивается на 100 МБ ~ каждую секунду.

Редактировать 2: В моей папке около 103 изображений, каждое из которых составляет ~ 100 КБ. Мне нужно решение, где не имеет значения, сколько изображений в папке. Кто-то порекомендовал открыть изображение, сделать мою магию, а затем закрыть его. Я не очень понимаю, что он имел в виду под «близким».

Может кто-нибудь порекомендовать другой подход? :)

Редактировать 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 ]

14 голосов
/ 05 февраля 2010

Вы не вызываете Dispose для ваших экземпляров Image. Также создайте свое изображение один раз, а затем извлеките данные.

Смотри также:

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

EDIT Если скопировал ваш код и протестировал его с моей библиотекой изображений. Мой средний Размер файла составляет 2-3 МБ на файл. Я выполнил вашу программу, и она сделала именно то, что и должна. GC сделал именно то, что я ожидал.

Объем памяти вашей Программы всегда составлял 11-35 МБ. Частный рабочий набор, размер фиксации был стабильным и составлял 43 МБ.

Я прервал программу после 1156 файлов с общим размером изображения 2,9 ГБ.

Так что должна быть еще одна причина для исключения нехватки памяти.

Вот мой вывод программы и код:

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);
            }
        }
    }
}
6 голосов
/ 05 февраля 2010

Вы должны освободить ресурсы, используемые при открытии объекта Image.

Либо вы можете вызвать Dispose, либо создать свое изображение в Using выражении

, например

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

И так как ваш класс содержит Image, вы должны реализовать интерфейс IDisposable, чтобы вы также могли использовать его в операторе using.

5 голосов
/ 05 февраля 2010

Несколько проблем, которые я провожаю. Во-первых, вы загружаете каждое изображение дважды с последующими вызовами к CalculatePictureWidth и CalculatePictureHeight. Во-вторых, кажется, вы никогда ничего не делаете с миниатюрой. В-третьих, вы должны вызывать Dispose для экземпляров Image, когда вы закончите сбор информации от них.

3 голосов
/ 05 февраля 2010

Файл изображения содержит дескриптор большого блока неуправляемых данных. Эти данные должны быть удалены, и вы делаете это, явно вызывая Image.Dispose. Так как вы не вызываете Dispose в методах CalculatePictureHeight и CalculatePictureWidth, файлы изображений хранятся в памяти, что вызывает ваши исключения OOM.

[Update]: Возможно, мне немного больше полезного фона, почему это на самом деле происходит. В конце концов, разве нет ГК, чтобы убрать этот беспорядок для нас? Да, и GC довольно эффективен. Это в конечном итоге также очистит объекты, которые нам пришлось утилизировать.

Класс Image содержит финализатор. Этот финализатор обеспечит очистку всех ресурсов, даже если вы забудете вызвать Dispose. Однако в данном случае вам не хватает памяти, что немедленно вызывает GC.Collect. Во время этого сбора GC находит все ваши изображения, на которые нет ссылок. Однако, поскольку GC не будет запускать метод финализатора немедленно, все ваши объекты будут переведены в Gen1 (они хранятся в памяти), как и все ваши ресурсы (собственная память). Таким образом, даже после запуска GC памяти все еще недостаточно, и вы получаете исключение OOM.

Вот почему вы всегда должны располагать объекты, которые реализуют IDisposable.

2 голосов
/ 05 февраля 2010

Вы не должны хранить все изображения в одном списке, вы должны делать то, что у вас есть, с одним изображением за раз и располагать каждое изображение после каждой итерации.

Проверка метода Image.Dispose ().

1 голос
/ 05 февраля 2010

Попробуйте смотреть ваши изображения по одному. Я нашел определенную причину .jpg в этом - у библиотек, управляющих изображениями, есть проблемы с определенными изображениями, не знаю почему. Но у нас есть некоторые файлы .jpg, сохраненные из фотошопа каким-то странным образом. Держу пари, ты найдешь это одно плохое яблоко.

1 голос
/ 05 февраля 2010

Насколько велики ваши файлы изображений? Сколько файлов в вашем каталоге? В зависимости от этих параметров я мог видеть, как вы можете получить OutOfMemory. Вы можете проверить свой подход. Вместо того, чтобы загружать все в память, вы должны загрузить каждую картинку по отдельности, выполнить действие и затем продолжить (после того, как вы удалили предыдущую картинку, конечно).

1 голос
/ 05 февраля 2010

Сколько картинок у вас в этой папке. Я вижу, как вы просматриваете все картинки, загружаете их в память и сохраняете их в виде списка. Это может быть причиной.

0 голосов
/ 24 января 2012

Я думаю, что проблема:

Если вы вызываете GetThumbnailImage с одним из аргументов ширины или высоты, равным -1, он также выдаст исключение OutOfMemoryException.

или

Если вы создаете объект Image с помощью Image.FromStream (например, MemoryStream), а затем закрываете поток (MSDN говорит, что не делать этого в течение всего времени жизни объекта), то вызываете этот метод (GetThumbnailImage) объект сгенерирует исключение OutOfMemoryException.

0 голосов
/ 05 февраля 2010

Вы можете сделать это?

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;
    }
}
...