使用流列表时出现内存问题

时间:2018-10-03 13:15:59

标签: c#

我正在处理图像处理项目,但遇到了问题。

我有一种方法,该方法将(图像的)流作为输入并返回包含所提供图像子部分的流的列表:

public List<System.IO.Stream> ImageProcessing(System.IO.Stream input){
    Bitmap inputAsBitmap = new Bitmap(input);
    // --- Applying miscellaneous filters ---

    List<Bitmap> subParts = GetSubParts(inputAsBitmap);
    inputAsBitmap.Dispose();

    List<System.IO.Stream> streams = new List<Stream>();
    for(int i = 0; i < subParts.Count; i++){
        MemoryStream memoryStream = new MemoryStream();
        subParts[i].Save(memoryStream, ImageFormat.Jpeg);
        streams.Add(memoryStream);
        subParts[i].Dispose();
    }
    return streams;
}

当我通过OpenFileDialog选择图像手动调用此方法时,一切正常:

using (Stream imageStream = File.OpenRead(filename)){
    List<Stream> streams = ImageProcessing(imageStream);
    for(int i = 0; i < streams.Count; i++){
        System.Drawing.Image.FromStream(streams[i]).Save();
        streams[i].Dispose();
    }
}

但是当我尝试选择一个文件夹并在for循环中调用它的每个图像时,都会抛出一个OutOfMemoryException

for(int i = 0; i < filenames.Count; i++){
    using (Stream imageStream = File.OpenRead(filenames[i])){
        List<Stream> streams = ImageProcessing(imageStream);
        for(int j = 0; j < streams.Count; j++){
            System.Drawing.Image.FromStream(streams[j]).Save();
            streams[j].Dispose();
        }
    }
}

在调查时,我发现拥有一条指令块需要花费大约20ms的时间来执行,可以清除内存,从而防止引发异常:

for(int i = 0; i < filenames.Count; i++){
    // --- A block of instructions placed here will prevent the exception ---
    using (Stream imageStream = File.OpenRead(filenames[i])){
        // -- Same code as above ---
    }
}

此外,在检查多个点的内存(使用Process.GetCurrentProcess().PrivateMemorySize64System.GC.GetTotalMemory())时,处置BitmapStream似乎并没有改变金额的已用内存。

关于如何摆脱这个OutOfMemoryException的任何提示?

您是否对以下事实有任何解释:在for循环中调用ImageProcessing方法时,如果在手动完成两次调用之间执行操作,则不会清除内存?


编辑:

这是我现在拥有的,但仍然无法正常工作:

for(int i = 0; i < filenames.Count; i++){
    using (Stream imageStream = File.OpenRead(filenames[i])){
        List<Stream> streams = ImageProcessing(imageStream);
        for(int j = 0; j < streams.Count; j++){
            using (var image = System.Drawing.Image.FromStream(streams[j]))
                image.Save();
            streams[j] = null;
            //System.GC.Collect();
        }
    }
}
//System.GC.Collect();

1 个答案:

答案 0 :(得分:3)

您可以做一些改善的事情。

首先,您应强制将应用程序作为64位应用程序运行,并在发布模式下运行该程序,因为调试模式可能会使该程序在实例上的保留时间更长。

然后,对System.Drawing.Image.FromStream(streams[j]).Save();的调用会返回一个图像,因此也必须将其丢弃。

最后,当我完成一些变量操作后,我不喜欢放null。 JIT / GC通常 知道何时不再使用变量。但是,对于您来说,stream[j]实例在整个过程中都保留在列表中。也许您可以尝试使用IEnumerableyield return来稍微改变程序的结构,以使实例不会在列表中停留太长时间。这里是关键点:

public IEnumerable<Bitmap> GetSubParts()
{
   ...
   // Assuming there is a loop somewhere:
   for(***)
   { 
      yield return ** Bitmap
   }
}

与ImageProcessing相同:

public IEnumerable<System.IO.Stream> ImageProcessing(System.IO.Stream input)
{
    using(Bitmap inputAsBitmap = new Bitmap(input))
    {
        // --- Applying miscellaneous filters ---

        foreach(var subPart in GetSubParts(inputAsBitmap))
        {
            MemoryStream memoryStream = new MemoryStream();

            using(subPart)
            {
                subPart.Save(memoryStream, ImageFormat.Jpeg);
            }

            yield return memoryStream;
        }
    }
}

最后:

for(int i = 0; i < filenames.Count; i++)
{
    using (Stream imageStream = File.OpenRead(filenames[i]))
    {
        foreach(var stream = ImageProcessing(imageStream))
        {
            using(stream)
            {        
                 using(var image = System.Drawing.Image.FromStream(stream))
                     image.Save();
            }
        }
    }
}