user415789
user415789

Reputation:

Run out of memory

I have some code here

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

and the Function SomeMethod is:

    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 is :

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 Opens a file in filepath that has about 400 line of image locations, I read them and draw something on them base on my calculations, I have not used any external library or any unsafe code. the question is why I run out of memory ??

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

when the program starts it uses about 20mb of memory, after calling Run the memory usage start increasing, if I run it for about 200 images the memory goes around 1.7Gb and Run function finished the work and memory usage get back to 20mb

------------EDIT------------ saving Bitmap B in MemoryStream is becuase graphic can not use Indexed pixel format images. The main question is what is garbage collector doing here?? I have no objects that should remained in memory.

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

the exception is :

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: 

and the exception thrown at line :

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

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

I also add the line GC.Collect(); after

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

the same error happened again.

Upvotes: 5

Views: 10421

Answers (6)

asishn
asishn

Reputation: 1

usage of GC.Collect() or GC.WaitForPendingFinalizers() is not the correct way of fixing this issue. You are getting this error as you still have Bitmap object. So you have to delete it.

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

Add the following code to the class level without fail.

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

Upvotes: 0

dthorpe
dthorpe

Reputation: 36072

Your code makes and keeps at least 4 copies of the bitmap in memory at the same time. Bitmap B, the memory stream, possibly two copies for bitmap T, and possibly another for Graphics g. How big are these bitmaps?

You don't really need Bitmap B in memory after the B.Save, but it will remain in memory until the end of the using clause that constructs it. You should consider reordering your code so that objects are released as soon as they are no longer needed. Namely, move the stuff after B.Save() outside of the B using clause at a minimum.

Saving the bitmap to memory stream seems suspicious too. You load the bitmap from disk into bitmap B, then save B to the memory stream (the call to SomeMethod doesn't modify the bitmap), then load the bitmap from the memory stream. Why? Bitmap T should be identical to Bitmap B.

And whats with the new Bitmap(new Bitmap(Image.FromStream(...)))? That seems pointlessly wasteful of memory.

Try reordering your code something like this (untested):

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

This arrangement allows objects like Bitmap B and memorystream M to be released as soon as possible. Your nested using statements keep them alive a lot longer than needed, which will prevent the GC from collecting them if the GC were to kick in in the middle of all this. If the GC did kick in in the middle of all this, any objects that could not be collected because they're still referenced by a live variable (or enclosing using clause) will be pushed to the next older heap generation group, which means it will be that much longer before they will be considered for collection again.

Note that Bitmaps also involve unmanaged resources - GDI bitmap handles, which consume system memory outside of the .NET GC system and are not released until Bitmap.Dispose. So even if the bitmap object itself is not collected by the GC, the fact that we are calling Bitmap.Dispose a lot earlier in the execution flow should help reduce memory pressure since that will dispose of the GDI bitmap handle.

I don't think this will solve your memory issue completely, but it should help. When dealing with code that inherently involves large memory consumption, you need to take a more active role in managing when things get allocated and disposed. Using clauses are convenient, but try..finally clauses are more explicit and more precise, IMO.

Upvotes: 1

Guffa
Guffa

Reputation: 700152

Most likely becase you allocate a lot of objects that you don't dispose.

In this line:

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

The FromStream call allocates a bitmap that you never dispose. The inner Bitmap constructor creates another bitmap that you never dispose. It's only the outer Bitmap that is disposed.

Remember that the using block only disposes the objects that you allocate directly in that statement, not any object that you create and use as parameters for creating that object.

I don't see the logic in creating a bitmap from a bitmap from a bitmap, when you can just use the bitmap that you have loaded:

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

In this line:

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

you create a Pen object that is never disposed. Put that in a using block:

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

Upvotes: 14

user415789
user415789

Reputation:

I just replace GC.Collect() with GC.WaitForPendingFinalizers() and the Problem has been solved.

Upvotes: 0

Tony Abrams
Tony Abrams

Reputation: 4673

You are most likely running out of memory due all the images.

I have a little screen capture application that I've been playing with and when only doing 100 images it takes 2gb+ of memory (physical + page file).

Try scaling it down to see what happens when you only do 10 images.

Upvotes: 1

Oded
Oded

Reputation: 498904

Sounds like you are compiling or running your application to be 32bit or using ANY CPU.

Compile it to be 64bit and you will not run into the 2GB process limit of 32bit applications.

Upvotes: 1

Related Questions