How to Handle the "Out of Memory" Exception? [C#]

Shaitan00

Junior Contributor
Joined
Aug 11, 2003
Messages
358
Location
Hell
I am creating a game [C#], my first couple of versions worked perfectly fine on my many computers and my friends ...
Then I went and made it larger by added objects (more on the map) and loading more pictures when I draw, running more bots concurrently, etc - pretty much made it more massive and complex (newer version of the game)...

Great for me and all my computers (still works fine) but oddly enough my friend can no longer player it (I must admit his computer is far less powerful with much less ram)...
Still I would have expected the game to run "slower" on other PCs (mind you this is a real wimpy GDI+ game) but when he loads the game (doesn't even get to play for 1 second) in the phase where I generate my map by creating objects in each cell of the level it fails/crashes with the following error:

************** Exception Text **************
System.OutOfMemoryException: Out of memory.
at System.Drawing.Image.FromFile(String filename, Boolean
useEmbeddedColorManagement)
at System.Drawing.Image.FromFile(String filename)
at BattleGrounds.BaseObject.get_ImgExtWall()
at BattleGrounds.BaseObject.GetObjectImage()
at BattleGrounds.BaseObject..ctor(ObjectType _objectType, Int32 xStart,
Int32 yStart)
at BattleGrounds.BaseLevel.GenerateLevel()
at BattleGrounds.frmBoard.frmBoard_Load(Object sender, EventArgs e)
at System.Windows.Forms.Form.OnLoad(EventArgs e)
at System.Windows.Forms.Form.OnCreateControl()
at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
at System.Windows.Forms.Control.CreateControl()
at System.Windows.Forms.Control.WmShowWindow(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ContainerControl.WndProc(Message& m)
at System.Windows.Forms.Form.WmShowWindow(Message& m)
at System.Windows.Forms.Form.WndProc(Message& m)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

As stated, a slow down would have been expected 'maybe" but an all out out-of-memory failure was unexpected - how am I supposed to manage such a situation?
I want him to be able to play (and all others) - it should be able to run on most PCs (again simple GDI+ game based on cells that load images)....

I am totally at a lost on how to deal with memory management issues, wouldn't even know where to start ... I ran it on my PC and there doesn't seem to be a noticable memory leak... and I am only loading more Objects...
Any ideas, hints, and help would be greatly appreciated, thanks
 
Last edited:
That is a very interesting proposition - to "share" the same Monster Image? might lower the load - but how could I "share" something like that? (making it static? I tried and it didn't change my stats .. from what I can see on my PC).

Thing is I have a Class that takes a TYPE in the construtor and loads the appropriate image into memory - this class is used to fill the level map of my game - so I create like 50 of the objects, 20 walls, 10 bricks, etc... and each time I load the image into memory by doing something like the following:

Code:
private Bitmap image = null;

...
if (Type == Wall)
	image = ImgWall;
....

public Image ImgWall
{
	get 
	{ return Image.FromFile("Images\\wall.bmp"); }
}

Currently the Mem Usage and VM Size on my PC are ~24megs/~13megs (which doesn't seem obseesive).

What indication will I have that it is better? (shouldn't I be looking at reducing the Mem Usage and VM Size? Or is there a better indication?)

Also - you mention the way I dispose of my images, all I do is set the object=null or overwrite it with another object (for example if a WALL is destroyed and becomes a FLOOR I just set that cell=null - shouldn't the destructor automatically handle disposing? And if it was NOT disposing wouldn't I see my Memory usage and VM size increase like a memory leak? because I am pretty stable at the above mentioned stats)....

Thanks,
 
It might be worth checking the Images have been correctly installed on the individual PC correctly - or replace them with the correct images again as a precaution.

Image.FromFile can throw an OutOfMemoryException when attempting to load a corrupt / invalid file and even when the file is correct but you do not have the correct permissions to read it.
 
One trick would be to NOT load a NEW image from the file for every cell. Rather, load up all of your images one time and reference them as needed. I would guess you have some set of "tile" images that are drawn for each cell - typically a simple 2 dimensional array.

If you're only using the images to draw the appropriate picture in each cell, you don't need to keep loading images from disk - load once and use that same variable everywhere to draw. You can change your code pretty simply by doing the following. I added a wallImage which is only loaded once and then returned by the property. Your other code shouldn't have to change at all.

I must admin, I'm not sure why you have a class level variable called "image" set to ImgWall - why not just use ImgWall instead of image, put the "if" logic into a function that returns the appropriate property? Or simply make the private variable a variable that's local to the method it's used in?

C#:
private Bitmap image = null;
private Bitmap wallImage = null;

...
if (Type == Wall)
	image = ImgWall;
....

public Image ImgWall
{
    get 
    { 
        if (wallImage == null) wallImage =Image.FromFile("Images\\wall.bmp");
        return wallImage;
    }
}

You may also consider using Resource files if you have VS 2005. You can put an image in the resource file and give it a name, like wall, then use it just like a class.

-ner
 
Last edited:
Nerseus >> That is exactly the type of solution I am trying to achieve, problem is I can't seem to pin-point how...

Your way (in the code snipplet) still has each cell loading its own copy of the image... Imagine it is something like:

Code:
public class base
{
   private Bitmap image = null;
   private Bitmap wallImage = null;
 
   ...
   if (Type == Wall)
 	   image = ImgWall;
   ....
 
   public Image ImgWall
   {
       get 
       { 
           if (wallImage == null) wallImage =Image.FromFile("Images\\wall.bmp");
           return wallImage;
       }
   }
}

public class main_part
{
   ...
   // Put Walls on all sides
   for (all i's)
      level[i, 0] = new base(Type.Wall);

   for (all j's)
      level[0,j] = new base(Type.Wall);
}

As you can see, I am filling the array with objects of type base ... Using your code each object still creates and holds it's own instance of image and on top of that each will also now hold its own copy of wallimage, doesn't this actually make it much worse?

Isn't there a way to SHARE wallImage for all the Objects of Type = Wall?
So instead of having 50 floors I only load ONE, and instead of 20 walls I only load ONE, etc...

I thought STATIC would do it but it doesn't seem to be working for me (just tried making the functions themselves static)...

Am I looking at this wrong?
Is there a better way?
Thanks,
 
I think Nerseus might have meant to put his code into another class, either that or he mis-understood some of your code.

Why not load all images statically. It would be very simple.
C#:
public static class Images{
    public static Bitmap Wall = new Bitmap("Images\\wall.bmp");
    public static Bitmap Path = new Bitmap("Images\\path.bml");
}
Elsewhere, when you need to grab an image, you can grab it from the images class.
C#:
this.WhateverPropertyOrField = Images.Wall;
It provides a very straightforward way to load the images once and use them all around. It is basically an equivalent to using the resource designer in VS 2005/Express.
 
marble_eater >> Great idea !!! But I keep getting some errors with the sample code you provided - specifically for the use of the term "STATIC" in the class definition "public STATIC class Images"

Error Message:
LoadStaticImages.cs(18): The modifier 'static' is not valid for this item
Any ideas?

So instead I tried the following code in a seperate class:
Code:
public class LoadStaticImages
{
   public static Bitmap ImageWall = new Bitmap("Images\\wall.bmp");
   public static Bitmap ImageFloor = new Bitmap("Images\\floor.bmp");
   public static Bitmap ImageExtWall = new Bitmap("Images\\extwall.bmp");
   public static Bitmap Imagebrick = new Bitmap("Images\\brick.bmp");
   public static Bitmap ImageSpace = new Bitmap("Images\\space.bmp");
}

And in my code I now use it as follows:
Code:
...
Image image;
if (Type=Wall)
   image = ImgWall;
return image;
...  

public Image ImgWall
{
   get 
   { return LoadStaticImages.ImageWall; }
}
So now each object (of type base) will return the LOADED IMAGE (from LoadStaticImages) each time I need to draw that image on screen, I removed the stored bitmap per-object and just return the loaded ones when called instead (I expected this to make a HUGE difference but it really didn;t change my Memory Usage or VM size at all).... Any ideas as to why not? This new code a) doesn't store an IMAGE per object and only returns the loaded static ones each when I redraw the map - isn't this much more efficient?

Thanks,
 
Last edited:
You might want to change the definitions to be public readonly static as this will prevent the objects being re-assigned.

The idea of a static class is new in .Net 2.0, if you are on an older version then you will not be able to declare a static class.

Although the overall memory may not change you are probably putting less strain on the garbage collector as it will not need to be releasing memory as often.
 
PlausiblyDamp >> Couple of questions regarding your reply ...
- I am currently using VS 2005 with .NET 1.1 and I would love to migrate to .NET 2.0 but not sure what that implies, do I just need to go on Microsoft and download the .NET 2.0 redistributables or do I also need a newer version of Visual Studios (like 2007 or something?)

- So IDEALLY would be to make the class STATIC (which means I need to migrate to .NET 2.0) ?

- "You might want to change the definitions to be public readonly static as this will prevent the objects being re-assigned." ==> You mean use a SINGLETON?

- "Although the overall memory may not change you are probably putting less strain on the garbage collector as it will not need to be releasing memory as often." ==> Does this mean it will not improve the Out_Of_Memory error thrown on my friends PC? Because my goal is so that he is able to play (because he was able to play before when it wasn't so complex).

Thanks for all the help,
 
Shaitan00 said:
- I am currently using VS 2005 with .NET 1.1 and I would love to migrate to .NET 2.0 but not sure what that implies, do I just need to go on Microsoft and download the .NET 2.0 redistributables or do I also need a newer version of Visual Studios (like 2007 or something?)
Are you sure you have VS 2005? To the best of my knowledge it isn't possible to use .Net 1.1 with VS 2005. And if you were running 2.0 you shouldn't have had an error for declaring a static class.

Shaitan00 said:
- So IDEALLY would be to make the class STATIC (which means I need to migrate to .NET 2.0) ?
I believe PlausiblyDamp was simply explaining to you why you might have got an error with the code marble_eater provided. I see no reason the class being static would be of a major benefit.

Shaitan00 said:
- "You might want to change the definitions to be public readonly static as this will prevent the objects being re-assigned." ==> You mean use a SINGLETON?
I believe he simply means declare the bitmap as a readonly object so you can't accident overwrite it.
C#:
public readonly static Bitmap = new Bitmap("blah");
 
If you are using VS 2005 then you are using .Net 2.0, .net 1.1 used VS 2002 / 2003.

The readonly keyword doesn't make the class behave like a singleton on it's own, however it can be useful if you are using the singleton pattern. The main use for it would be in preventing other code doing things like
C#:
LoadStaticImages.ImageSpace = null; //or similar

In terms of fixing the memory error it might not remove the underlying problem for the particular individual - however having a more efficient memory model may resolve the problem; the best thing to do is try it and see - even if the problem doesn't go away you have at least got a better / more stable code base for the future.
 
Back
Top