Исключение из-за нехватки памяти при добавлении данных в базу данных с использованием Entity Framework - PullRequest
0 голосов
/ 22 января 2020

У меня есть 27000 изображений, которые хранятся в папке и которые необходимо добавить в базу данных с помощью EntityFramework. У меня есть код

        var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);

        foreach(var file in files)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                Image img = Image.FromFile(file);

                var imgRes = ResizeImage(img, ImageSettings.Width, ImageSettings.Height);

                MemoryStream memoryStream = new MemoryStream();

                img.Save(memoryStream, ImageFormat.Png);

                var label = Directory.GetParent(file).Name;
                var bytes = memoryStream.ToArray();

                memoryStream.Close();

                db.Add(new ImageData { Image = bytes, Label = label });

                img.Dispose();
                memoryStream.Dispose();
                imgRes.Dispose();
            }
        }

, он работает только при наличии менее 10000 изображений, в противном случае я получаю исключение «Недостаточно памяти». Как я могу загрузить свои 27000 изображений в базу данных.

Ответы [ 2 ]

1 голос
/ 22 января 2020

Прежде всего, этот код не работает с сущностями или объектами, поэтому использование ORM вообще не помогает. Это не вызывает OOM, но только делает код на lot медленнее.

Реальная проблема в том, что MemoryStream на самом деле является оберткой вокруг буфера. Когда буфер заполнен, новый перераспределяется с двойным размером, исходные данные копируются, а старый буфер удаляется. Увеличение буфера размером 50 МБ таким способом приводит к перераспределению lot , log2 (50M). Это фрагментирует свободную память до такой степени, что среда выполнения больше не может выделять достаточно большой непрерывный буфер. Это приводит к тому, что OOM также имеют объекты List<T>, а не только MemoryStreams.

Быстрое исправление заключается в передаче ожидаемого размера в качестве емкости потока через конструктор MemoryStream (Int32) . Это сокращает перераспределение и , сохраняя много циклов ЦП. Число не обязательно должно быть точным, достаточно большим, чтобы избежать слишком большого количества мусора:

using(Image img = Image.FromFile(file))
using(var imgRes = ResizeImage(img, ImageSettings.Width, ImageSettings.Height))
using(var memoryStream = new MemoryStream(10_000_000))
{
    img.Save(memoryStream, ImageFormat.Png);
    var label = Directory.GetParent(file).Name;
    var bytes = memoryStream.ToArray();

    db.Add(new ImageData { Image = bytes, Label = label });
}

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

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

var buffer=new byte[100_000_000];
using(Image img = Image.FromFile(file))
using(var imgRes = ResizeImage(img, ImageSettings.Width, ImageSettings.Height))
using(var memoryStream = new MemoryStream(buffer))
{
    img.Save(memoryStream, ImageFormat.Png);
    var label = Directory.GetParent(file).Name;
    var bytes = memoryStream.ToArray();

    db.Add(new ImageData { Image = bytes, Label = label });
}
0 голосов
/ 22 января 2020

Старайтесь не создавать ApplicationContext в foreach l oop:

using (ApplicationContext db = new ApplicationContext())
{
    foreach(var file in files)
    {        
        using (MemoryStream ms = new MemoryStream())
        using(Image img = Image.FromFile(file))
        using(var imgRes = ResizeImage(img, ImageSettings.Width, ImageSettings.Height))
        {
            var imgRes = ResizeImage(img, ImageSettings.Width, ImageSettings.Height);
            MemoryStream memoryStream = new MemoryStream();
            img.Save(memoryStream, ImageFormat.Png);
            var label = Directory.GetParent(file).Name;
            var bytes = memoryStream.ToArray();
            db.Add(new ImageData { Image = bytes, Label = label }); 
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...