C# System.Drawing.Image.FromStream()内存不足异常

C# System.Drawing.Image.FromStream()内存不足异常,c#,image,image-processing,out-of-memory,C#,Image,Image Processing,Out Of Memory,我有一个应用程序,它处理和重新调整图像的大小,有时在长时间的迭代过程中,我会从MemoryException中得到 我将图像作为文件流存储在数据库中,在处理过程中,我需要将它们保存到临时物理位置 我的模型: [Table("Car")] public class Car { [... some fields ...] public virtual ICollection<CarPhoto> CarPhotos { get; set; } } [Table("CarPho

我有一个应用程序,它处理和重新调整图像的大小,有时在长时间的迭代过程中,我会从MemoryException中得到

我将图像作为文件流存储在数据库中,在处理过程中,我需要将它们保存到临时物理位置

我的模型:

[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; }
}
我看过类似的线索,但没有一个答案适用于我的情况

其中一个答案建议使用Image.FromStream(),但这正是我正在做的

我很有信心我所有的照片都是有效的。异常似乎是随机发生的,大多数情况下是在处理较大的文件时发生的,但较小的文件也会发生这种情况。有时一个图像会失败,但下一次会处理得很好

就我所见,我正在正确地处理所有东西(内存流、图像和位图)

这项工作被枪声所触发。这可能会导致问题吗?

您的程序占用了大量内存,获得OOM肯定不是意外的。它的确切死亡地点是不可预测的。但是是的,创建位图可能是它最先死亡的地方。按顺序说明最可能的原因:

   foreach (var car in cars)
你驾驶的汽车数量没有明显的上限。每辆车都有多个图像,您似乎将这些图像存储在内存中(photos.Binary)。或者换句话说,这个程序迟早会消亡,因为它需要处理越来越多的汽车。取得成功的唯一方法是串行处理汽车,而不是在内存中成批处理汽车。可能是令人不快的建议,强烈建议在64位模式下运行此代码

   using (var memoryStream = new MemoryStream(photo.Binary))
内存流是一个大问题,它的底层缓冲区很可能存储在大型对象堆中。LOH没有被压缩,重复重新分配MemoryStream可能会导致该堆变得支离破碎。迟早你会从一个足以容纳下一张照片的缓冲区的洞里钻出来。非常随机,具体发生时间取决于您之前处理的照片类型。您将希望重新使用该对象,而不是一次又一次地重新分配它。只需在进入循环之前创建一次,将容量设置为一个非常大的数字

    Graphics.FromImage(bitmap).DrawImage(image, 0, 0, newWidth, newHeight);

需要处理该图形对象。像处理其他对象一样使用using语句



总的来说,您的程序的真正问题在于它无法扩展,并且在需要处理不断增加的数据集时总是会崩溃。修复这可能是一个非常重要的重新写入,您几乎肯定希望翻转忽略位,并在64位进程中利用可用的地址空间。为了拯救硬件,它现在可以随时使用。

您可以通过以下方法避免执行所有位图代码(这可能会解决内存问题):

var resized = image.GetThumbnailImage(newWidth, newHeight,() => false, IntPtr.Zero);
resized.Save(directory + filePath);

我也遇到了同样的问题。问题是,即使使用USING语句,图像对象也不会立即处理

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

        }
IEnumerable mcontrols=this.panel1.Controls.Cast()。其中(n=>n.GetType()==typeof(PreviewImage));
//由于原因不明,处置后需要多次清理;
while(mcontrols.Count()!=0)
{
foreach(预览mcontrols中的图像pi)
{
pi.Dispose();
}
mcontrols=this.panel1.Controls.Cast(),其中(n=>n.GetType()==typeof(PreviewImage));
如果(mcontrols==null){
打破
}
}

即使在第一次循环后仍然在我的面板1中,我也没有关闭控件。是的,这是一个可怕的解决方法,但它对我有效

我认为您需要熟悉windbg并分析崩溃转储以找到解决方案。在处理之前,不要将所有图像加载到内存中。FILESTREAM存储的优点是,您可以返回一个流,而不是一个BLOB。使用SqlFileStream一次加载和处理一个图像。该图形对象也需要处理,请像处理其他对象一样使用。如果这不能解决问题,并且您的程序由于地址空间碎片而死亡,那么位图是困难的对象,因为它们通常非常大。强烈建议在64位模式下运行此代码。如果这不是一个选项,那么重新使用MemoryStream对象而不是重复地重新创建它将是明智的。我已经尝试过了——不创建不必要的位图和图形对象是有意义的,但我最终得到了同样的例外。看来我需要按照汉斯的回答来做。谢谢你的全面回答。我喜欢重复使用内存流的想法。没想到重新分配它会是一个如此大的问题。
 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;
            }

        }