无法通过使用语句清除内存

Cant clear memory from using statement

我已经尝试处理并使用 'using' 语句来防止高内存使用。但是,当我的记忆无法清除所有内容时,这让我感到困惑。

下面是我将位图图片保存到Excel工作表中的方法。

我运行 2次模拟

  1. 我在它进来之前删除了位图列表,它使用了 200mb+。[​​=34=]
  2. 我加载了位图列表,在此方法之前它使用了 600+mb(正常)。在此方法中进入循环后,它又增加了 600mb,总计 1.2GB。退出此方法后,它会下降到 600mb+。我错过了什么,因为我觉得内存应该只有 200mb - 300mb 左右。

在代码中,我使用了 2 个 'using' 语句来启用自动处理图像和流。

感谢您的帮助!

private void SaveBitMapIntoExcelSht(ref List<Bitmap> bmpLst, IXLWorksheet wksheet, int pixH)
    {
        string inputCell;
        using (MemoryStream stream = new MemoryStream())
        {
            for (int i = 0; i < bmpLst.Count; i++)
            {
                Console.WriteLine(GC.GetTotalMemory(true));
                inputCell = "A" + (30).ToString();
                using (Image image = Image.FromHbitmap(bmpLst[i].GetHbitmap()))//Convert bitmap to hbitmap and store as a stream to save directly into the excel file.
                {
                    Console.WriteLine(GC.GetTotalMemory(true));
                    // Save image to stream.
                    image.Save(stream, ImageFormat.Png);
                    IXLPicture pic = wksheet.AddPicture(stream, XLPictureFormat.Png);
                    pic.MoveTo(wksheet.Cell(inputCell));

                    pic.Delete();
                    pic.Dispose();
                    image.Dispose();
                    GC.Collect();
                }
            }
        }
        foreach (var bmp in bmpLst)
        {
            bmp.Dispose();
        }
        bmpLst.Clear();
        Dispose();
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
    }

编辑:回答 对于那些感兴趣的人,您可能会发现下面的代码有效。 之前我在 using 语句中得到了 hbitmap 但没有对它的引用,因此我创建了一个 var 句柄以便我可以删除它。

将此添加到您的 class

[System.Runtime.InteropServices.DllImport("gdi32.dll")] public static extern bool DeleteObject(IntPtr hObject);

using (MemoryStream stream = new MemoryStream())
        {
            for (int i = 0; i < bmpLst.Count; i++)
            {
                Console.WriteLine(GC.GetTotalMemory(true));
                inputCell = "A" + (i * numberOfCells + 1).ToString();

                //using (Image image = Image.FromHbitmap(bmpLst[i].GetHbitmap()))
                var handle = bmpLst[i].GetHbitmap();
                using (Image image = Image.FromHbitmap(handle))//Convert bitmap to hbitmap and store as a stream to save directly into the excel file.
                {
                    // Save image to stream.
                    image.Save(stream, ImageFormat.Png);
                    pic = wksheet.AddPicture(stream, XLPictureFormat.Png);
                    pic.MoveTo(wksheet.Cell(inputCell));

                    try
                    {
                        var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
                    }
                    finally
                    {
                        DeleteObject(handle);
                    }
                }
            }
        }

来自 GetHBitmap

的文档

You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object. For more information about GDI bitmaps, see Bitmaps in the Windows GDI documentation.

FromHbitmap

的文档

The FromHbitmap method makes a copy of the GDI bitmap; so you can release the incoming GDI bitmap using the GDI DeleteObject method immediately after creating the new Image.

即您正在创建一个您永远不会删除的 GDI 对象。所以你需要在 IntPtr

上调用 pInvoke 函数 DeleteObject
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject([In] IntPtr hObject);

或者使用其他方式将位图转换为图像。

根据经验,如果您看到任何返回 IntPtr 它可能代表一些非托管资源,如果是这样,您需要格外小心检查是否需要手动 dispose/delete东西。

另一个经验法则是,如果您怀疑内存泄漏,请使用内存分析器。它可能没有发现这个问题,因为它涉及指向非托管内存的指针,但这应该是调查的第一步。