System.Drawing.Image.FromStream() 内存不足异常
Out Of Memory exception on System.Drawing.Image.FromStream()
我有一个应用程序,它处理图像并调整图像大小,在长时间的迭代过程中偶尔会出现 OutOfMemoryException。
我将我的图像作为文件流存储在数据库中,在处理过程中我需要将它们保存到一个临时的物理位置。
我的模特:
[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);
}
}
}
}
}
我看过 this 类似的帖子,但 none 的答案似乎适用于我的情况。
其中一个答案建议使用 Image.FromStream(),但这就是我正在做的事情。
我非常有信心我所有的图片都是有效的。异常似乎是随机发生的,最常见于处理较大的文件时,但它也发生在较小的文件上。有时一张图片会失败,但下一次会处理的很好。
据我所知,我正在正确处理所有内容(内存流、图像和位图)。
作业正在由 Hangfire 触发。这可能会导致问题吗?
你的程序吃掉了很多内存,出现OOM当然不是意外。它究竟会死在哪里是不可预测的。但是,是的,创建位图是它可能首先死亡的地方。按顺序解决最可能的原因:
foreach (var car in cars)
您处理的汽车数量没有明显的上限。每辆车都有多个图像,您似乎将这些图像存储在内存中 (photos.Binary)。或者换句话说,这个程序 保证 迟早会死掉,仅仅是因为它需要处理越来越多的汽车。取得成功的唯一方法是连续处理汽车,而不是在内存中对它们进行批处理。可能是不愉快的建议,运行强烈建议在 64 位模式下使用此代码。
using (var memoryStream = new MemoryStream(photo.Binary))
那个内存流是个大问题,它的底层buffer很有可能存放在Large Object Heap中。 LOH 未压缩,反复重新分配 MemoryStream 可能会使该堆变得碎片化。迟早你 运行 从一个大到足以容纳下一张照片缓冲区的洞里出来。非常随机,具体发生时间取决于您之前处理的照片类型。您将希望重新使用该对象,而不是一遍又一遍地重新分配它。在进入循环之前创建一次,将 Capacity 设置为一个不错的大数字。
Graphics.FromImage(bitmap).DrawImage(image, 0, 0, newWidth, newHeight);
需要释放该 Graphics 对象。像处理其他对象一样使用 using 语句。
总的来说,您的程序的真正问题在于它根本无法扩展,并且在需要处理不断增加的数据集时总是会崩溃。修复它可能是一次非常重要的重写,您几乎肯定想要翻转忽略位并利用 64 位进程中的可用地址 space。硬件来拯救,今天很容易买到。
您可以避免执行所有位图代码(这可能会解决内存问题),只需使用:
var resized = image.GetThumbnailImage(newWidth, newHeight,() => false, IntPtr.Zero);
resized.Save(directory + filePath);
我有同样的问题 problem.The 即使您使用 USING statement.For 示例
图像对象也不会立即处理
IEnumerable<Control> mcontrols = this.panel1.Controls.Cast<Control>().Where(n => n.GetType() == typeof(PreviewImage));
// for unclear reason after the dispose needs many times to clear everything;
while (mcontrols.Count() != 0)
{
foreach (PreviewImage pi in mcontrols)
{
pi.Dispose();
}
mcontrols = this.panel1.Controls.Cast<Control>().Where(n => n.GetType() == typeof(PreviewImage));
if (mcontrols == null) {
break;
}
}
即使在我的 panel1 中仍然存在第一个循环之后,我也没有放弃 controls.Yes 这是一个可怕的解决方法,但它对我有用
我有一个应用程序,它处理图像并调整图像大小,在长时间的迭代过程中偶尔会出现 OutOfMemoryException。
我将我的图像作为文件流存储在数据库中,在处理过程中我需要将它们保存到一个临时的物理位置。
我的模特:
[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);
}
}
}
}
}
我看过 this 类似的帖子,但 none 的答案似乎适用于我的情况。
其中一个答案建议使用 Image.FromStream(),但这就是我正在做的事情。
我非常有信心我所有的图片都是有效的。异常似乎是随机发生的,最常见于处理较大的文件时,但它也发生在较小的文件上。有时一张图片会失败,但下一次会处理的很好。
据我所知,我正在正确处理所有内容(内存流、图像和位图)。
作业正在由 Hangfire 触发。这可能会导致问题吗?
你的程序吃掉了很多内存,出现OOM当然不是意外。它究竟会死在哪里是不可预测的。但是,是的,创建位图是它可能首先死亡的地方。按顺序解决最可能的原因:
foreach (var car in cars)
您处理的汽车数量没有明显的上限。每辆车都有多个图像,您似乎将这些图像存储在内存中 (photos.Binary)。或者换句话说,这个程序 保证 迟早会死掉,仅仅是因为它需要处理越来越多的汽车。取得成功的唯一方法是连续处理汽车,而不是在内存中对它们进行批处理。可能是不愉快的建议,运行强烈建议在 64 位模式下使用此代码。
using (var memoryStream = new MemoryStream(photo.Binary))
那个内存流是个大问题,它的底层buffer很有可能存放在Large Object Heap中。 LOH 未压缩,反复重新分配 MemoryStream 可能会使该堆变得碎片化。迟早你 运行 从一个大到足以容纳下一张照片缓冲区的洞里出来。非常随机,具体发生时间取决于您之前处理的照片类型。您将希望重新使用该对象,而不是一遍又一遍地重新分配它。在进入循环之前创建一次,将 Capacity 设置为一个不错的大数字。
Graphics.FromImage(bitmap).DrawImage(image, 0, 0, newWidth, newHeight);
需要释放该 Graphics 对象。像处理其他对象一样使用 using 语句。
总的来说,您的程序的真正问题在于它根本无法扩展,并且在需要处理不断增加的数据集时总是会崩溃。修复它可能是一次非常重要的重写,您几乎肯定想要翻转忽略位并利用 64 位进程中的可用地址 space。硬件来拯救,今天很容易买到。
您可以避免执行所有位图代码(这可能会解决内存问题),只需使用:
var resized = image.GetThumbnailImage(newWidth, newHeight,() => false, IntPtr.Zero);
resized.Save(directory + filePath);
我有同样的问题 problem.The 即使您使用 USING statement.For 示例
图像对象也不会立即处理 IEnumerable<Control> mcontrols = this.panel1.Controls.Cast<Control>().Where(n => n.GetType() == typeof(PreviewImage));
// for unclear reason after the dispose needs many times to clear everything;
while (mcontrols.Count() != 0)
{
foreach (PreviewImage pi in mcontrols)
{
pi.Dispose();
}
mcontrols = this.panel1.Controls.Cast<Control>().Where(n => n.GetType() == typeof(PreviewImage));
if (mcontrols == null) {
break;
}
}
即使在我的 panel1 中仍然存在第一个循环之后,我也没有放弃 controls.Yes 这是一个可怕的解决方法,但它对我有用