问题描述
我有一个应用程序,该程序可以处理图像并调整大小,有时在长迭代中偶尔会出现OutOfMemoryException.
I have an application, which processes and re-sizes images and occassionally during long iterations I get OutOfMemoryException.
我将图像作为文件流存储在数据库中,并且在处理过程中需要将其保存到临时物理位置.
I store my images in the database as filestream and during processing I need to save them to a temporary physical location.
我的模特:
[Table("Car")]
public class Car
{
[... some fields ...]
public virtual ICollection<CarPhoto> CarPhotos { get; set; }
}
[Table("CarPhoto")]
public class CarPhoto
{
[... some fields ...]
public Guid Key { get; set; }
[Column(TypeName = "image")]
public byte[] Binary { get; set; }
}
处理过程大致如下:
foreach (var car in cars)
{
foreach (var photo in car.CarPhotos)
{
using (var memoryStream = new MemoryStream(photo.Binary))
{
using (var image = Image.FromStream(memoryStream)) // this is where the exception is thrown
{
var ratioX = 600.00 / image.Width;
var newWidth = (int)(image.Width * ratioX);
var newHeight = (int)(image.Height * ratioX);
using (var bitmap = new Bitmap(newWidth, newHeight))
{
Graphics.FromImage(bitmap).DrawImage(image, 0, 0, newWidth, newHeight);
bitmap.Save(directory + filePath);
}
}
}
}
}
我看过与此类似线程,但所有答案似乎都不适用于我的情况.
I've looked at this similar thread, but none of the answers seems to apply in my case.
答案之一是建议使用Image.FromStream(),但这仍然是我正在做的事情.
One of the answers suggests using Image.FromStream(), but it's what I'm doing anyway.
我非常有信心我所有的图像都是有效的.异常似乎是随机发生的,通常是在处理较大的文件时发生的,但对于较小的文件也是如此.有时一个图像会失败,但是下次可以很好地处理.
I'm quite confident all my images are valid. The exception seems to be occurring randomly, most often when it comes to processing larger files, but it happens for smaller ones as well. Sometimes one image will fail, but it will be processed fine the next time.
据我所知,我正在正确处理所有内容(内存流,图像和位图).
As far as I can see, I'm disposing of everything correctly (memory stream, image and bitmap).
该工作正在被Hangfire取消.可能会引起问题吗?
The job is being trigerred by Hangfire. Could that possibly cause a problem?
推荐答案
您的程序占用了很多内存,获得OOM肯定不是意外的.确切的死点是无法预测的.但是,是的,创建位图可能是最先死亡的地方.按顺序解决最可能的原因:
Your program eats a lot of memory, getting OOM is certainly not unexpected. Exactly where it will die is unpredictable. But yes, creating the bitmap is where it is likely to die first. Addressing the most likely reasons in order:
foreach (var car in cars)
您处理的汽车的数量没有明显的上限.每辆汽车都有多张图像,您似乎将这些图像存储在内存中(照片:二进制).换句话说,保证该程序早晚死去,仅仅是因为它需要处理越来越多的汽车.取得成功的唯一方法是依次处理汽车,而不是将其全部批量存储在内存中.强烈建议您以64位模式运行此代码,这可能是不愉快的建议.
There is no obvious upper-bound on the number of cars you handle. Each car has multiple images and you appear to store those images in memory (photos.Binary). Or in other words, this program is guaranteed to die sooner or later, simply because it needs to handle an ever-more increasing number of cars. Only way to get ahead is by processing cars serially instead of batching them all in memory. Likely to be unpleasant advice, running this code in 64-bit mode is highly recommended.
using (var memoryStream = new MemoryStream(photo.Binary))
那个内存流是一个大问题,它的底层缓冲区很可能存储在大对象堆中.LOH没有被压缩,因此反复重复分配MemoryStream使得该堆有可能变得碎片化.迟早您会用尽一个足够大的孔来容纳下一张照片的缓冲区.随机性的确切发生时间取决于您之前处理过的照片种类.您将要重复使用该对象,而不是一遍又一遍地重新分配它.只需在进入循环之前创建一次,然后将容量"设置为一个很大的数字即可.
That memory stream is a big problem, its underlying buffer is very likely to be stored in the Large Object Heap. The LOH is not compacted, re-allocating the MemoryStream repeatedly makes it likely that this heap is getting fragmented. Sooner or later you run out of a hole that's big enough to fit the buffer for the next photo. Exceedingly random, exactly when that happens depends on what kind of photos you processed earlier. You'll want to re-use the object instead of re-allocating it over and over again. Just create it once before you enter the loop, set the Capacity to a nice big number.
Graphics.FromImage(bitmap).DrawImage(image, 0, 0, newWidth, newHeight);
需要处理Graphics对象.像对待其他对象一样使用 using 语句.
That Graphics object needs to be disposed. Use the using statement like you did with the other objects.
总的来说,您程序的真正问题在于它根本无法扩展,并且在需要处理不断增长的数据集时总是会崩溃.要解决此问题,这可能是非常重要的重写,您几乎肯定要翻转它的ignore位,并在64位进程中利用可用的地址空间.抢救硬件,今天就可以买到.
By and large, the real problem with your program is that it is simply doesn't scale and will always fall over when it needs to deal with an increasing dataset. Fixing that can be a very significant re-write, you almost surely want to flip the ignore bit on that and take advantage of the available address space in a 64-bit process. Hardware to the rescue, it is readily available today.
这篇关于System.Drawing.Image.FromStream()上的内存不足异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!