认为我有内存泄漏

时间:2016-03-14 20:29:38

标签: c# winforms memory-leaks

嘿得到这个错误:

  

发生了'System.OutOfMemoryException'类型的异常   System.Drawing.dll但未在用户代码中处理

     

其他信息:内存不足。

它出现在DrawImage调用

中的以下方法中
/// <summary>
        /// Resize the image to the specified width and height.
        /// </summary>
        /// <param name="image">The image to resize.</param>
        /// <returns>The resized image.</returns>
        public Bitmap ResizeImage(Image image, System.Drawing.Size newSize)
        {
            var destRect = new Rectangle(0, 0, newSize.Width, newSize.Height);
            var destImage = new Bitmap(newSize.Width, newSize.Height);

            destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

            using (var graphics = Graphics.FromImage(destImage))
            {
                graphics.CompositingMode = CompositingMode.SourceCopy;
                graphics.CompositingQuality = CompositingQuality.HighQuality;
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

                using (var wrapMode = new ImageAttributes())
                {
                    wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                    graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
                }
            }

            return destImage;
        }

我不确定它为什么会发生,我多次调用此方法,结果转换为Base64字符串并存储在ObservableCollection中。

/// <summary>
        /// Convert Image and Resize
        /// </summary>
        /// <param name="loc"></param>
        /// <returns></returns>
        public async Task<string> GenerateThumbnailBinary(string loc)
        {
            return await Task<string>.Factory.StartNew(() =>
            {
                Image image = Image.FromFile(loc, true);

                // Figure out the ratio
                double ratioX = (double)Properties.Settings.Default.ThumbnailWidth.Width / (double)image.Width;
                double ratioY = (double)Properties.Settings.Default.ThumbnailWidth.Height / (double)image.Height;
                // use whichever multiplier is smaller
                double ratio = ratioX < ratioY ? ratioX : ratioY;

                System.Drawing.Size newSize = 
                new System.Drawing.Size(
                    (int)(image.Width * ratio), 
                    (int)(image.Height * ratio));

                Image resized = ResizeImage(image, newSize);

                return ImageToBase64(resized, ImageFormat.Jpeg);
            });
        }

我还通过绑定到Collection并使用转换器将Base64字符串转换回Bitmap,将每个字符串显示为图像,这只是UI显示已转换的内容。

我的问题将从哪里开始?当我在UI上显示太多图像并使用转换器将字符串转换为图像时,我是否可以尝试在内存中存储太多图像?

下面图片中的高点显然是在运行方法循环时,但它似乎仍然保持高于最后运行方法之前,这有帮助吗? enter image description here

编辑: 这是启动任务并运行方法的循环。

// Generate List of images to upload
                var files = Directory.EnumerateFiles(sel.Name, "*.*", SearchOption.AllDirectories)
                        .Where(s => s.EndsWith(".jpeg") ||
                        s.EndsWith(".jpg") ||
                        s.EndsWith(".png") ||
                        s.EndsWith(".JPG"));
                int b = 1;

                if (files.Count() > 0)
                {
                    /// <summary>
                    /// Resize Images
                    /// </summary>
                    /// Set current Task first
                    UploadTask = Steps[0].Message;
                    try
                    {
                        foreach (string item in files)
                        {
                            // Generate new name
                            string oldname = Path.GetFileNameWithoutExtension(item);
                            string newName = Common.Security.KeyGenerator.GetUniqueKey(32);
                            string t = await GenerateThumbnailBinary(item);

                            ImageUploadObjects.Add(
                                new ImageUploadObject { OldName = oldname,
                                    NewName = newName,
                                    ByteImage = t });

                            UploadProgress = (int)Math.Round((double)(100 * b / files.Count()));
                            b++;
                        }
                        // Complete
                        Steps[0].Complete = true;
                    }
                    catch(Exception e)
                    {
                        Steps[0].Error = e.InnerException.ToString();
                    }


                    /// <summary>
                    /// Move full resoluation images
                    /// </summary>
                    /// Set current Task first
                    UploadTask = Steps[1].Message;
                    try
                    {
                        foreach (string item in files)
                        {

                        }
                        // Complete
                        Steps[1].Complete = true;
                    }
                    catch (Exception e)
                    {
                        Steps[1].Error = e.InnerException.ToString();
                    }
                }
            }

编辑:

如何判断内存是否仍在使用?我在下面添加了另一张图片,第一张快照是在我执行方法之前,最后一张是在它完成时,2 - 4是在它运行时 enter image description here

2 个答案:

答案 0 :(得分:1)

我认为解决问题的最简单方法是以块/批次的方式完成。例如,如果您有100个文件,则创建100个任务,这些任务将文件内容加载到内存中。也许做10(或其他一些数字),一旦完成,做下一个10(或其他一些数字)。我相信这会解决你的问题。

还要确保在任何实现Disposable的类上调用Dispose,即Image和Bitmap等。

除了上述内容之外,这里简要介绍了您要做的事情: 1.读取目录并获取所有文件。 2.为每个文件创建缩略图图像。 3.将缩略图添加到内存中的集合。 4.将图像传输到另一个位置。

对于上面的第2项,我不会将所有缩略图保留在内存中。即使我需要在UI中显示它,我将合并分页并根据需要拉它们。

答案 1 :(得分:0)

Image resized = ResizeImage(image, newSize)未被处理。因为分配的内存在终结器线程运行之前不会被释放,所以你可能会在整个地方泄漏内存。

Image image = Image.FromFile(loc, true);
...

Image resized = ResizeImage(image, newSize);
image.Dispose();
string base64Image = ImageToBase64(resized, ImageFormat.Jpeg);
resized.Dispose();
return base64Image;