Использование памяти и манипулирование изображениями - PullRequest
0 голосов
/ 21 октября 2019

TL; DR;Изображения, преобразованные в base64string, занимают огромную площадь ОЗУ в куче больших объектов.

У меня есть некоторый код в службе Windows, который использует изображения наших продуктов, загруженные пользователями, стандартизирует их в формат веб-класса (они будут загружать 10 МБ растровые изображения), и выполняет некоторые другие вещи, такие как изменение их размера в квадрат и добавление пробелов.

Затем он преобразует их в строку base64 для загрузки их в нашу среду хостинга через rest. Среда требует, чтобы это было сделано таким образом, я не могу использовать URL-адреса. Когда я это делаю, они со временем сохраняются в куче больших объектов и стремительно увеличивают использование ОЗУ программы.

Как мне обойти эту проблему?

Вот код,

private void HandleDocuments(IBaseProduct netforumProduct, MagentoClient client, bool isChild)
{
    if (netforumProduct.Documents == null) { return; }

    for (int idx = 0; idx < netforumProduct.Documents.Count; idx++)
    {
        JToken document = netforumProduct.Documents[idx]["Document"];
        if (document == null) { continue; }

        string fileName = document["URL"].ToString();

        // Skip photos on child products (the only identifier is part of the url string)
        if (fileName.ToLower().Contains("photo") && isChild) { continue; }

        using (HttpClient instance = new HttpClient {BaseAddress = client.NetforumFilesBaseAddress})
        {
            string trimStart = fileName.TrimStart('.');

            string base64String;

            using (Stream originalImageStream = instance.GetStreamAsync("iweb" + trimStart).Result)
            {
                using (MemoryStream newMemoryStream = new MemoryStream())
                {
                    using (Image img = Image.FromStream(originalImageStream))
                    {
                        using (Image retImg = Utility.Framework.ImageToFixedSize(img, 1200, 1200))
                        {
                            retImg.Save(newMemoryStream, ImageFormat.Jpeg);
                        }
                    }

                    newMemoryStream.Position = 0;

                    byte[] bytes = newMemoryStream.ToArray();
                    base64String = Convert.ToBase64String(bytes);
                }
            }

            // MediaGalleryEntry is a simple class with a few string properties
            MediaGalleryEntry mge = new MediaGalleryEntry
            {
                label = "Product_" + netforumProduct.Code + "_image_" + idx,
                content = new MediaGalleryContent
                {
                    base64_encoded_data = base64String,
                    name = "Gallery_Image_" + idx
                },
                file = trimStart
            };

            this.media_gallery_entries.Add(mge);
        }
    }
}

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

1 Ответ

1 голос
/ 21 октября 2019

TL; DR;Изображения, преобразованные в строку base64string, занимают огромное количество ОЗУ в куче больших объектов

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

Затем он преобразует их в строку Base64 для загрузки их в нашу среду хостинга через отдых. Среда требует, чтобы это было сделано таким образом, я не могу использовать URL-адреса. Когда я это делаю, они со временем сохраняются в куче больших объектов и стремительно увеличивают объем используемой оперативной памяти программы. "Base64 неэффективен, но не добавляет к этому много. + 25% IIRC.

Большие вопросы, если вы действительно видите проблему здесь или только неправильно читаете объем памяти? @CodeCaster выяснил, что вы сохранили ссылку (которая является реальной проблемой и одним из немногих способов, которыми вы можете получитьутечка памяти в .NET вообще), но даже если вы ее потеряете, эта строка все равно будет оставаться в памяти некоторое время.

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

  • вызовы GC.Collect();, которые обычно не должныОн используется в производительном коде, только для отладки, если у вас есть эталонная утечка памяти
  • опасность OOM Expection
  • некоторые альтернативные режимы GC, например, серверный

Все, что я могу вам сказать, это будет работать в конечном итоге . Но я не думаю, что вам нужно знать точное время обязательно.

...