内存耗尽

时间:2010-12-07 18:05:09

标签: c# .net memory

我这里有一些代码

   private void Run()
    {
        MyClass c = new MyClass();
        c.Load(somepath1);
        using (StreamReader sr = new StreamReader(filepath))
        {
            string line = string.Empty;
            while ((line = sr.ReadLine()) != null)
            {
                using (Bitmap B = new Bitmap(line))
                {
                    Point p = SomeMethod(ref c, new Point());
                    using (MemoryStream ms = new MemoryStream())
                    {
                        B.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
                        using (Bitmap T = new Bitmap(new Bitmap(Image.FromStream(ms))))
                        using (Graphics g = Graphics.FromImage(T))
                        {
                            g.DrawEllipse(new Pen(Brushes.Red, 4), p.X - 5, p.Y - 5, 10, 10);
                            FileInfo fi = new FileInfo(somepath2);
                            T.Save(Path.Combine(somepath3, fi.Name));
                        }
                    }
                }
            }
        }
    } 

和函数SomeMethod是:

    Point SomeMethod(ref MyClass c, Point mid)
    {
        float[] Mat = new float[9];
        Point p;

        c.Method1(Mat);
        c.Method2(Mat, out p);

        return p;
    }

MyClass是:

public class MyClass
{
    public void Method1(float[] Mat, out Point point)
    {
        //calculation point from values in Mat
    }

    public void Method2(float[] Mat)
    {
        //Do some Operation in Mat
    }

    public void Load(string FileName)
    {
        //Do Some Data Loading From a small file about 400 byte
    }
}

StreamReader sr在文件路径中打开一个文件,该文件包含大约400行图像位置,我根据我的计算读取它们并在它们上绘制一些东西,我没有使用任何外部库或任何不安全的代码。 问题是为什么我的内存不足?

------------- EDIT --------------------

当程序启动时它使用大约20mb的内存,在调用Run后内存使用率开始增加,如果我运行大约200个图像,内存大约1.7Gb并且Run函数完成工作并且内存使用量回到20mb

------------ ------------ EDIT 在MemoryStream中保存位图B是因为图形不能使用索引像素格式图像。 主要问题是垃圾收集器在这做什么? 我没有应留在记忆中的物品。

---------- EDIT ----------------

例外是:

System.OutOfMemoryException was unhandled
  Message=Out of memory.
  Source=System.Drawing
  StackTrace:
       at System.Drawing.Graphics.CheckErrorStatus(Int32 status)
       at System.Drawing.Graphics.DrawImage(Image image, Int32 x, Int32 y, Int32 width, Int32 height)
       at System.Drawing.Bitmap..ctor(Image original, Int32 width, Int32 height)
       at System.Drawing.Bitmap..ctor(Image original)
       at WindowsFormsApplication1.Form1.buttonrun1_Click(Object sender, EventArgs e) in C:\Users\hamidp\Desktop\WindowsFormsApplication1\WindowsFormsApplication1\Form1.cs:line 115
       at System.Windows.Forms.Control.OnClick(EventArgs e)
       at System.Windows.Forms.Button.OnClick(EventArgs e)
       at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
       at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
       at System.Windows.Forms.Control.WndProc(Message& m)
       at System.Windows.Forms.ButtonBase.WndProc(Message& m)
       at System.Windows.Forms.Button.WndProc(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
       at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.Run(Form mainForm)
       at WindowsFormsApplication1.Program.Main() in C:\Users\hamidp\Desktop\WindowsFormsApplication1\WindowsFormsApplication1\Program.cs:line 17
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

并在行处抛出异常:

using (Bitmap T = new Bitmap(new Bitmap(Image.FromStream(ms))))

------------------- EDIT -----------------------

我还在

之后添加了行GC.Collect();

while ((line = sr.ReadLine()) != null)

同样的错误再次发生。

6 个答案:

答案 0 :(得分:14)

最有可能因为你分配了许多你没有处置的对象。

在这一行:

using (Bitmap T = new Bitmap(new Bitmap(Image.FromStream(ms))))

FromStream调用会分配一个您从未处理过的位图。内部Bitmap构造函数创建了另一个您从未处理过的位图。它只是处置的外部Bitmap

请记住,using块仅处理您在该语句中直接分配的对象,而不是您创建的任何对象,并将其用作创建该对象的参数。

当您只使用已加载的位图时,我没有看到从位图创建位图的逻辑:

using (Bitmap T = (Bitmap)Image.FromStream(ms))

在这一行:

g.DrawEllipse(new Pen(Brushes.Red, 4), p.X - 5, p.Y - 5, 10, 10);

创建一个永不丢弃的Pen对象。把它放在一个使用块中:

Using (Pen red = new Pen(Brushes.Red, 4)) {
  g.DrawEllipse(red, p.X - 5, p.Y - 5, 10, 10);
}

答案 1 :(得分:1)

听起来您正在将应用程序编译或运行为32位或使用ANY CPU

将其编译为64位,您将不会遇到32位应用程序的2GB进程限制。

答案 2 :(得分:1)

由于所有图像,你很可能内存不足。

我有一个小屏幕捕获应用程序,我一直在玩,当只做100张图像需要2GB内存(物理+页面文件)。

尝试将其缩小以查看仅拍摄10张图像时会发生什么。

答案 3 :(得分:1)

您的代码同时在内存中制作并保留至少4个位图副本。位图B,内存流,可能是位图T的两个副本,可能是Graphics g的另一个副本。这些位图有多大?

在B.Save之后,你真的不需要内存中的Bitmap B,但是它将保留在内存中,直到构造它的using子句结束。您应该考虑重新排序代码,以便在不再需要对象时立即释放它们。也就是说,将B.Save()之后的东西至少移到B using子句之外。

将位图保存到内存流似乎也很可疑。您将位图从磁盘加载到位图B,然后将B保存到内存流(对SomeMethod的调用不会修改位图),然后从内存流加载位图。为什么?位图T应与位图B相同。

新的Bitmap(新的Bitmap(Image.FromStream(...)))是什么?这似乎毫无意义地浪费了记忆。

尝试重新排序您的代码(未经测试):

while ((line = sr.ReadLine()) != null) 
{ 
    Bitmap T = null;
    try
    {
        MemoryStream ms = new MemoryStream()) 
        try
        { 
            Bitmap B = new Bitmap(line);
            try
            { 
                Point p = SomeMethod(ref c, new Point()); 
                B.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
            }
            finally 
            {
                B.Dispose();
            }

            T = new Bitmap(Image.FromStream(ms));
        }
        finally
        {
            m.Dispose();
        }

        using (Graphics g = Graphics.FromImage(T)) 
        { 
            g.DrawEllipse(new Pen(Brushes.Red, 4), p.X - 5, p.Y - 5, 10, 10); 
            FileInfo fi = new FileInfo(somepath2); 
            T.Save(Path.Combine(somepath3, fi.Name)); 
        }
    }
    finally
    {
        if (T != null)
        {
            T.Dispose();
        } 
    }
} 

这种安排允许像Bitmap B和memorystream M这样的对象尽快发布。您的嵌套使用语句使它们比所需的时间长得多,如果GC在所有这些中间启动,这将阻止GC收集它们。如果GC确实在所有这些中间启动,那么由于它们仍然被实时变量(或封闭使用子句)引用而无法收集的任何对象将被推送到下一个较旧的堆生成组,这意味着它将会再考虑再次收集它们。

请注意,位图还涉及非托管资源 - GDI位图句柄,它占用.NET GC系统之外的系统内存,直到Bitmap.Dispose才会释放。因此,即使GC没有收集位图对象本身,我们在执行流程中更早地调用Bitmap.Dispose这一事实应该有助于减少内存压力,因为这将处理GDI位图句柄。

我认为这不会完全解决你的记忆问题,但它应该会有所帮助。在处理本身涉及大量内存消耗的代码时,您需要在管理事务分配和处理时采取更积极的角色。使用子句很方便,但是尝试..最终条款更明确,更精确,IMO。

答案 4 :(得分:0)

我只是将GC.Collect()替换为GC.WaitForPendingFinalizers(),问题已经解决。

答案 5 :(得分:0)

GC.Collect()GC.WaitForPendingFinalizers()的使用不是解决此问题的正确方法。您仍然有Bitmap对象,您收到此错误。所以你必须删除它。

try
{
    IntPtr intPtrHBitmap = IntPtr.Zero; 
    BitmapSource _Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(intPtrHBitmap, IntPtr.Zero, System.Windows.Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}
finally
{
    DeleteObject(intPtrHBitmap);
    _Source = null;
}

将以下代码添加到类级别中。

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