Получение исключения "из памяти" в этой относительно простой программе

вот моя фотография.класс ЗС:

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:ImagesPictureFolder";

        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: Я открыл Диспетчер задач, чтобы увидеть использование памяти, и когда я нажимаю Кнопка для запуска метода я замечаю, что использование памяти увеличивается на 100mb~ каждую секунду.

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

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

Edit 3: До сих пор получив исключение из памяти, я изменил код на картинке.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 ответов


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

Читайте также:

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

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

память ваша программа всегда была о частном рабочем наборе 11-35 MB, размер фиксации был стабилен на 43 MB.

я прервал программу после 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);
            }
        }
    }
}

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


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


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

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

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

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


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

Проверьте изображение.Метод Dispose ().


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


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


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


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

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

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

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

или

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


несколько человек упомянули о вызове dispose после того, как сделали с изображениями. Однако этого недостаточно, и что-то .net framework будет делать для вас по мере увеличения использования памяти. Поскольку вы добавили каждое изображение в связанный список, .NET framework не может его удалить, поскольку вы неявно сохраняете ссылку.

Это означает, что все данные, хранящиеся в классе picture, сохраняются во время цикла foreach. Из фрагмента кода я не вижу, что это такое, он может включать сжатый или даже несжатая версия образов, которая могла бы объяснить взрыв вашей памяти.

[Edit] удалена часть anwer, которую другие указали как не относящуюся к делу; я предположил, что класс изображения является классом изображения XNA.