【问题标题】:Memory issue when working with list of stream使用流列表时的内存问题
【发布时间】:2018-10-03 23:26:43
【问题描述】:

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

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

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

在调查时,我发现执行大约需要 20 毫秒的指令块可以清理内存,从而防止引发异常:

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();

【问题讨论】:

标签: c#


【解决方案1】:

您可以做一些事情来改进。

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

然后对 System.Drawing.Image.FromStream(streams[j]).Save(); 的调用返回一个 Image,因此它也必须被释放。

最后,当我完成一些变量时,我不喜欢使用null。 JIT / GC 通常知道何时不再使用变量。但是,在您的情况下,stream[j] 实例在整个过程中都保留在列表中。也许您可以尝试使用IEnumerableyield return 稍微改变程序的结构,这样实例就不会在列表中停留太久。以下是重点:

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

与图像处理相同:

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

【讨论】:

    猜你喜欢
    • 2015-05-27
    • 2012-07-20
    • 1970-01-01
    • 1970-01-01
    • 2021-10-18
    • 1970-01-01
    • 2012-02-22
    • 1970-01-01
    • 2018-10-14
    相关资源
    最近更新 更多