views:

190

answers:

3

My CF application has a very customized UI, using lots of images as UI elements. The UI feels a lot more smooth when these bitmaps are kept in memory. When they're loaded on demand, the UI is slow and I can see the buttons appearing one by one, which looks very poor. For a long time this went pretty well, but recently I've found the application nearly uses all the memory it can get, which is 32MB iirc. I then started using the remote performance monitor to see if I could find any clear memory hogs.

As it turns out, getting a useful snapshot of the GC heap using RPM is hard: close before I'm likely to receive out of memory exceptions, requesting a snapshot causes a native exception to be thrown instantly. I can manage to find a GC snapshot once in a while though. I saved one here: http://files.zzattack.org/misc/ramis.gclog and a screenshot here: http://files.zzattack.org/images/ramisgcsnapshot.png To me it doesn't look all that troublesome, by far the largest object is a byte array containing my resources file (about 3MB full of PNG images). Alltogeher, 3643304b (about 3.5MB) of memory is used. These images are spread over UI elements in about 20 different forms. I'm don't know what impact seperate threads have on memory usage, but about 5-6 threads are running simultaneously, of which at least 4 are in a blocked state 95% of the time.

In the program, when I attempt to download a 2MB gzipped file, I will always receive an OutOfMemoryException. When I call GC.GetTotalMemory(false), I see that I am indeed attempting to allocate more than is currently available. Calling GC.Collect and trying again doesn't 'solve'/postpone my problem either.

I would like some advice on how to tackle my problem. I absolutely do want the bitmaps in memory, but perhaps I could limit the amount of slots available for bitmaps, keeping only the most frequently needed ones in memory and loading others on demand. This is probably a long shot, but perhaps I could request the OS to reserve more memory for me? I know for sure that the application will only run on devices that have enough RAM available anyway. Any help is appreciated, thanks in advance.

A: 

"getting a useful snapshot of the GC heap using RPM is hard" Maybe you can try some profiler for .NET CF, I have heard of EQATEC profiler if that helps you with the threads.

My approach to avoiding OOM errors is to not design for an MDI(Multiple Document Interface) type application, have only one form in memory) and setting to null all objects after they have been used (specially the "heavier ones" like XMLDocument).

Also, this might be relevant: http://stackoverflow.com/questions/297697/outofmemoryexception-when-creating-a-large-bitmap-in-cf-net

Abhijeet Kashnia
Caching the forms and instead of closing/recreating them on the fly results in a much much smoother transition when switching forms. Therefor I want to keep my design with the heavily cached forms. I've used EQATEC profiler before, but as I recall, it doesn't log memory footprints. The first line of their product description would confirm this: "The EQATEC Profiler is a code profiler, not a memory profiler."
Frank Razenberg
+1  A: 

If you're using the same images on multiple forms, you should consider to keep those Bitmaps as static variables in a class that is accessible by all your forms.

public class AppBitmaps
{
  public static Bitmap LogoBitmap = new Bitmap(...);

  public static Bitmap ButtonBitmap = new Bitmap(...);
}

public class Form1
{
  public Form1()
  {
    this.Control1.Image = AppBitmaps.LogoBitmap;
  }
}

That way you only keep one instance of that image in memory, which should cut down the overall memory usage.

tomlog
Thanks for your comment. In the old situation, I did many calls to ResourceManager.GetObject() to load my bitmaps. Since I needed very minor localization and scaling options (i.e. sometimes a different image WVGA/VGA resolutions), everything was obtained through a static helper function. As such it was very easy to add a caching mechanism like you proposed in this helper function, which led to saving a few bitmaps in memory. For now it'll suffice, so thanks!
Frank Razenberg
A: 

If that is a snap shot of your application working at full tilt then I would suggest:

  • It's another app that is running
  • Your app pinvokes into an unmanaged memory leak
  • The OS (customised CE?) has a leak
  • or something else
Quibblesome
In fact, my device has lots of RAM available. I'm using a standard WM6 professional device, have none but the essential processes running, and my code has only very few P/Invoke functions which are usually called just once. I think this instead has to do with the 32MB address space limit that winmo apps need to deal with.
Frank Razenberg
I thought that limit didn't apply on WM6? I know it definitely does on WM5. Either way I forgot that you also need to add the size of the GC Heap and various CLR caches to your score (that you can get from the profiler). Then the size of the .dlls (uncompressed) ontop of that to get your final score.
Quibblesome