内存使用和操作图像
Memory usage and manipulating images
TL;DR;转换为 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);
}
}
}
它不是最好的代码,可能没有高度优化,但它是我能做的最好的。
TL;DR; Images converted to base64string have huge RAM footprint in large object heap
是的,这显然是真的。所有图像都很大。压缩方法仅适用于存储和传输。但是当图像加载到内存中 - 用于显示或进一步处理时 - 所有压缩步骤 必须 被撤消。这是与他们一起工作的人常犯的错误。
It then converts them to a Base64 string to upload them into our hosting environment via rest. The environment requires it be done this way, i cannot use URLS. When I do this, they get stored on the large object heap and the program's RAM usage skyrockets over time."
Base64 is ineffective, but will not add a lot to this. +25% IIRC.
如果您真的在这里看到问题,或者只是误读了内存占用量,那么大问题是什么? @CodeCaster 发现你保留了一个引用(这是一个真正的问题,也是你可以在 .NET 中获得内存泄漏的几种方法之一),但即使你丢失了这些,这个字符串仍然会在内存中保留一段时间.
.NET 使用 GarbageCollection 内存管理方法。这种方法有一个问题:当 GC 收集时,所有其他访问同一托管区域的线程都必须暂停。因此,GC 是 - 由于缺乏更好的术语 - 非常 lazy with 运行ning。如果在应用程序关闭时它只 运行s 一次,那是最理想的情况。唯一能让它更早到达 运行 的是:
- 对
GC.Collect();
的调用,通常不应在生产代码中使用,仅在出现引用内存泄漏时用于调试
- OOM 预期的危险
- 一些可选的 GC 模式,特别是服务器模式
我只能告诉你,它最终会 运行 。但我认为你不一定需要知道确切的时间。
TL;DR;转换为 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);
}
}
}
它不是最好的代码,可能没有高度优化,但它是我能做的最好的。
TL;DR; Images converted to base64string have huge RAM footprint in large object heap
是的,这显然是真的。所有图像都很大。压缩方法仅适用于存储和传输。但是当图像加载到内存中 - 用于显示或进一步处理时 - 所有压缩步骤 必须 被撤消。这是与他们一起工作的人常犯的错误。
It then converts them to a Base64 string to upload them into our hosting environment via rest. The environment requires it be done this way, i cannot use URLS. When I do this, they get stored on the large object heap and the program's RAM usage skyrockets over time." Base64 is ineffective, but will not add a lot to this. +25% IIRC.
如果您真的在这里看到问题,或者只是误读了内存占用量,那么大问题是什么? @CodeCaster 发现你保留了一个引用(这是一个真正的问题,也是你可以在 .NET 中获得内存泄漏的几种方法之一),但即使你丢失了这些,这个字符串仍然会在内存中保留一段时间.
.NET 使用 GarbageCollection 内存管理方法。这种方法有一个问题:当 GC 收集时,所有其他访问同一托管区域的线程都必须暂停。因此,GC 是 - 由于缺乏更好的术语 - 非常 lazy with 运行ning。如果在应用程序关闭时它只 运行s 一次,那是最理想的情况。唯一能让它更早到达 运行 的是:
- 对
GC.Collect();
的调用,通常不应在生产代码中使用,仅在出现引用内存泄漏时用于调试 - OOM 预期的危险
- 一些可选的 GC 模式,特别是服务器模式
我只能告诉你,它最终会 运行 。但我认为你不一定需要知道确切的时间。