0

TL;博士; 转换为 base64string 的图像在大型对象堆中占用大量 RAM。

我在 Windows 服务中有一些代码使用用户上传的我们的产品图像,将它们标准化为网络级格式(他们将上传 10MB 位图),并执行其他一些操作,例如将它们调整为正方形并添加空白填充。

然后它将它们转换为 base64 字符串,以通过 rest 将它们上传到我们的托管环境中。环境要求这样做,我不能使用 URLS。当我这样做时,它们会存储在大型对象堆中,并且随着时间的推移,程序的 RAM 使用率会飙升。

我该如何解决这个问题?

这是代码,

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

它不是有史以来最好的代码,可能不是高度优化的,但它是我能做的最好的。

4

1 回答 1

1

TL;博士; 转换为 base64string 的图像在大型对象堆中占用大量 RAM

是的,这显然是真的。所有图像都很大。压缩方法仅适用于存储和传输。但是当图像被加载到内存中 - 用于显示或进一步处理 - 所有压缩步骤必须撤消。这是与他们一起工作的人的常见陷阱。

然后它将它们转换为 Base64 字符串,以通过 rest 将它们上传到我们的托管环境中。环境要求这样做,我不能使用 URLS。当我这样做时,它们会存储在大型对象堆中,并且程序的 RAM 使用量会随着时间的推移而飙升。” Base64 无效,但不会增加太多。+ 25% IIRC。

如果您真的在这里看到问题,或者只是误读了内存占用,那么大问题是什么?@CodeCaster 发现您保留了一个引用(这是一个真正的问题,也是您可以在 .NET 中获得内存泄漏的少数几种方法之一),但即使您松开这些,该字符串仍会在内存中保留一段时间.

.NET 使用 GarbageCollection 内存管理方法。这种方法有一个问题:当 GC 收集时,访问同一托管区域的所有其他线程都必须暂停。结果,GC - 由于缺乏更好的术语 - 非常懒惰地运行。如果它只在应用程序关闭时运行一次,那是理想的情况。唯一可以让它更早运行的是:

  • GC.Collect();通常不应在生产代码中使用的调用,仅用于在出现引用内存泄漏时进行调试
  • OOM 预期的危险
  • 一些替代的 GC 模式,特别是像服务器模式这样的东西

我只能告诉你它最终会运行。但我认为您不一定需要知道确切的时间。

于 2019-10-21T12:37:36.743 回答