MemMaker for the .NET Compact Framework


Does everyone remember the good old days of DOS when we used to spend our time making more of the 640 KB memory space available for our drivers, programs, TSRs and even Windows?  Things like QEMM, HIMEM.SYS and EMM386.EXE bring back fond memories for me.  We had this one slot and Billg said we’d never need more than 640 KB.


DOSMem


Some of us even switched to OS/2 which could give us 740 KB to our DOS sessions while providing them with preemptive multitasking.  Yes, I could run multiple DOS games in multiple windows simultaneously with no degradation.  Wow, now I had a bunch of crash-protected slots each with 740 KB of memory.


dos


 


If you fast forward to today, you’ll see that Windows CE 5.0 and Windows Mobile 6.x shares some commonalities with their forefathers from the 80’s and 90’s.  The 32-bit embedded operating system that we rely on to power our Windows phones is made up of a bunch of slots.  The mobile applications that you build run inside one of these slots and unlike DOS with its 640 KB memory space, your app gets 32 MB of virtual memory space.  But just like with DOS, you don’t get access to the whole space because other things like system DLLs are already eating into your free virtual memory. 


Many of you might not care because you build simple apps that use very little memory.  On the other hand, most of the people and organizations I work with build the largest, most memory-intensive applications ever seen on the mobile device.  Needless to say, these folks aren’t too pleased that they don’t get the whole 32 MB of virtual memory that’s coming to them.  They probably wish they a utility like QEMM or MemMaker to put things in high memory.


I recently met with a good friend of mine who wanted to share some interesting findings with me.  Keep in mind, not only do I consider this person and his colleagues to be some of the top Compact Framework developers in the world, his team members designed and developed of one of the world’s largest, most complex managed apps running on a Windows Mobile device.  Like many organizations that have built very large Windows Mobile applications, free virtual memory issues and the “DLL Crunch” have deprived this app from of all the memory it would like to have.  One of the architects on this “Dream Team” noticed that by keeping their application’s EXE empty and putting all the forms, code, resources, and data in managed DLLs, he reduced the amount of virtual memory his app uses inside its slot while at the same time taking advantage of memory outside the slot in the 1 GB shared memory area.


To help you visualize this, I’m going to show you 2 pictures of the Windows Mobile process slots running a Compact Framework application two different ways.  The virtual memory viewer you see running in the emulators below shows the 32 slots in the User space of the OS.  Everything in Red is free virtual memory, Blue is committed memory and Green is reserved.  Slot 1 is crammed full of ROM DLLs and you can’t help but notice the area of Blue at the top of every other slot.  That’s space out of everyone’s slot being used by system and other native DLLs which means nobody’s going to get their fair share of their 32 MB slot space.


On the left you’ll see a NETCF app called StandardExe.exe running in slot 14 of the operating system.  This simple managed EXE has a 2.25 MB bitmap bound to it as a resource and a single form that compiles to the same size as the bitmap inside it.  If you look at the picture on the left, you’ll see a 2.25 MB Blue area coming up from the bottom of slot 14.  This represents the space being taken up by the EXE.


StandardSM      OptimizedSM


On the right a NETCF app called OptimizedExe.exe running in slot 11 of the operating system.  This managed EXE is completely empty.  The Main function calls into a static class of a managed DLL and that’s it.  No mas.  This results in an EXE with a file size of 5 KB.  In the managed DLL we have the same 2.25 MB bitmap bound to it as well as a simple form.  This compiles into a 2.25 MB DLL called OptimizedDLL.dll.  When you look at the picture on the right, you’ll be hard-pressed to see any Blue area coming up from the bottom of slot 11.  A closer look reveals the 2.25 MB DLL is nowhere to be found either.


This is pretty cool and has the potential to unleash the largest, most powerful games and applications Windows phones have ever seen.  So the big question is, how is this happening?  Is it magic? 


Those of you who have read Steven Pratschner’s blog know that the Compact Framework memory maps your managed EXE and DLLs into the 1 GB shared memory area outside the slot your app is running which is cool.  What you may not know is that the OS automatically blocks out virtual memory at the bottom of your slot that’s the same size as your EXE.  So even though the CLR is in control of app execution and is giving you lots of love by putting your managed EXE up in the shared memory area, Windows CE takes away a valuable chunk of memory because it thinks that’s where your EXE is running.  Guess what, your app isn’t running there and it’s not native.  For those of you with giant managed EXEs, you’re losing out on a lot of virtual memory in your slot that could be put to good use.  So the first lesson here is to do what Brian did and make your EXE nothing but an empty stub used to launch your app which really lives inside managed DLLs.


Your empty EXE code should look like the following:


using System;


namespace OptimizedExe
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [MTAThread]
        static void Main()
        {
            OptimizedDLL.StartUp.Main();
        }
    }
}


Your DLL code should look like the following:


using System;
using System.Windows.Forms;


namespace OptimizedDLL
{
    public class StartUp
    {
        public static void Main()
        {
            Application.Run(new Main());
        }
    }
}


So now that you’ve learned how to instantly give your managed apps more memory by beating Windows CE at its own game, let’s talk about the curious case of your managed DLL.  If you’ve read Reed Robison’s blog discussion about Slaying the Virtual Memory Monster, you know that DLLs seem to take up everyone’s virtual memory from the top of the slot down which doesn’t sound too fair.  DLLs keep pushing their way down everyone’s slot causing something we call the “DLL Crunch” as free virtual memory get’s squeezed between the DLLs and EXEs.  I’ve got some good news for you.  Managed DLLs do not exhibit this same behavior.  In fact, not only do they not use up memory in all the other slots of your Windows phone, they don’t even push downward on the memory of your own slot.  How could this be?


Managed DLLs are not DLLs.  The CLR just treats them as files that it memory maps into the 1 GB shared memory area.  To the Compact Framework, managed EXE and DLL assemblies are just files full of IL that it maps outside your process slot.  So now you know where the 2.25 MB bitmap that we bound to OptimizedDLL.dll is.  It’s beyond the 32 MB barrier of your slot and therefore not using up your valuable memory. 


So if I follow this new pattern for NETCF development, will my slot ever have virtual memory allocated or do I get a free lunch?


While there’s no free lunch, you did get a buy one get one free discount.  The JIT compiler is running in your slot and it pulls in IL from the 1 GB space as needed to compile the current call stack.  Resources that aren’t designed to be compiled or executed will never be pulled down here.  The GC Heap is in your slot and that’s where your currently allocated Objects and instance variables are hanging out.  Your slot maintains a 64 KB Stack for every Thread your app spawns and the AppDomain Heap maintains a representation of the data structures found in your assembly’s IL.


image


So what are the big takeaways here?


You can eliminate the erroneous and wasted allocation of EXE virtual memory in your slot by following the pattern of using an empty stub managed EXE to kick off your application.  Windows CE will now only block out 5 KB of memory.


You can take better advantage of the 1 GB shared memory area by putting your entire application inside managed DLLs.  This will make your app a good neighbor by not creating the dreaded “DLL Crunch” for all the other apps on your Windows phone.  It also reduces the amount of memory that has to be allocated inside your slot.


This new pattern of managed development on the Windows Mobile platform is a true breakthrough in memory management.  Come join me at Tech Ed 2009 this May in Los Angeles for a complete deep dive on this new way of building memory-intensive games and applications.


– Rob



Comments (30)

  1. Um título destes seria digno de abertura de um qualquer telejornal nacional! Recebi hoje pelo Newsgator

  2. Great article!

    Where do we get that Virtual Memory mapper utility? That looks awesome.

  3. Check out the Rob’s blog post in which describes the technique that can help you to allocate less memory

  4. Great article!

    Where do we get that Virtual Memory mapper utility? That looks awesome.

  5. Paul Yao says:

    Rob —

    Cool tip!

    I wonder if there is time for me to get a link to your blog into my book. Hmm.

    Best,

    Paul

  6. I always knew that managed DLLs are handled by the OS as files and therefore loaded into the LMA (Large Memory Area) instead of wasting my process slot’s virtual memory (see my very first post ever :-), but I’ve never tested this incredibly simple idea…

    Thanks Rob for spreading it out!

    ~raffaele

  7. Jed says:

    This winds up being a nice side effect of creating reusable code – see the old Mobile CAB stuff, for example.

    Most companies building more than one app should tend towards this type of design anyway.

  8. Morten Damsgaard says:

    Hi Rob,

    Great article. I think your "empty" managed exe will take 64kb (and not 5kb) of virtual memory, because CE maps dll’s and exe’s into 64kb boundaries.

    – Morten

  9. dvescovi says:

    Does all this hold true only for Windows Mobile up to 6/6.5 which uses the Windows CE 5.0 Kernel? I am wondering what your take on this or even if its even applicable under CE 6, which has a major memory archicture shift. My guess is Windows Mobile will adapt the CE 6 kernel sooner or later as I assume the reasion for the rearchicture probably came from the WM team as they are the biggest customer.

    David Vescovi

  10. robtiffany says:

    You’re correct David.  This pattern is only needed for versions of Windows Mobile that run on top of Window CE 5.0.  Those versions include 5.0, 6, 6.1 and 6.5.  Windows CE 6.0 has a redesigned kernel that supports 32,000 processes and each process gets 2 GB of RAM.

    -Rob

  11. robtiffany says:

    Morten, as of Windows Mobile 6, the CE 5.0 kernel was altered so that Module (Executable) DLLs could align to 4K boundaries which saved a lot of memory.  File (Read-only) DLLs remained aligned to 64K boundaries though.

    -Rob  

  12. monxalo says:

    Greetings,

    Does this behavior applies to dynamic loaded assemblies?

    Using Assembly.Load ?

    – Gonçalo

  13. I twittered this awhile back, but appreciating not everyone twitters I wanted to share this on my blog

  14. There is a lot of good stuff going on a Tech-Ed this week in LA. &#160; In you are here, come by and

  15. Andrew Konovalov says:

    Hi, Rob!

    I’ve recently translated your article into Russian and here is one interesting comment from an experienced guy:

    ==

    >> But just like with DOS, you don’t get access to the whole space

    >> because other things like system DLLs are already eating

    >> into your free virtual memory.

    Using the following function calls

    SetKMode(1); SetProcPermissions(0xffffffff);

    allows a developer to write anywhere (getting access to the whole space). It’s better not to use other 32Mb because there is XIP, so it can lead to hard reset.

    Translating address space of other processes can be achieved either using MapPtrToProcess() or appending dwVMBase value from Process structure (which can be fetched from aInfo[KINX_PROCARRAY] array of KData structure). The address is static and depends on architecture, but for ARM is always the same.

    Enjoy!

    ==

  16. Andrew Konovalov says:

    Here is also a good illustration of full address space mapping: http://i.msdn.microsoft.com/Aa450572.mm2(en-us,MSDN.10).gif

  17. Andrew Konovalov says:

    Short amendment regarding not using "other 32Mb". It really meant to be the particular memory range  0x2000000-0x4000000 (the XIP location).

  18. Now that the Windows Mobile 6.5 DTK is available, you are probably starting to discover some of the new

  19. simon.tamman says:

    Brilliant, and it just so happens that due to my rather pedantic architecture hat I have already done this (without even realising the virtual memory saving feature it provides). Our .exe just creates an instance of "App" and runs. 🙂

    Nice to know this though. It may help some other projects here!

    Reggies

    Jax

  20. robtiffany says:

    I’m just glad to see folks from our mobile community are observant and curious and therefore point out bugs in our system.  It’s also nice to see people put forth workarounds to help everyone else.  Raffaele Limosani in Italy was on to this issue over a year ago when he made it public on his blog.  He knew that managed exe’s whould take up slot space equal to their file size and that managed dll’s wouldn’t, which leads to the workaround of putting all your code in dll’s.  The only reason we’re even talking about this issue is because Windows Mobile 6.x still uses our older version of Windows CE 5.0 and not our current version where this has been fixed.  Thanks to Brian,  Raffaele and all those inside Microsoft who have made subtle memory issues clearer to our developers.

  21. Faiyaz says:

    Great finding sir.  Thanks for sharing the info.  Hope some of my issues will now be resolved.

  22. Sean Solt says:

    Hi Rob!

    I need a little support. I have a really huge .NET cf application. I fight with the memory limit for a while. I tried your method and used the memory mapper program to watch the changes but there were nothing changed. I put my program completly to a DLL as you advised and call it from an "empty" exe but the memory map remained the same as before the change. What can be the problem? (using WM5).

    Thank: Sean

  23. Walter says:

    Sounds neat, but didn’t seem to do anything for me either.  Maybe there are some other things going on here that we cannot see.  

  24. Sesham says:

    Hi,

    this is really a cool article, thanks for posting.

    I tried to use this technique on a sample application , I moved all the code to a managed dll.(code in the dll  keeps creating bitmaps dynamically in a thread)  

    dll code

      private void eatMem_Click(object sender, EventArgs e)

           {

               //if (screenBuffer != null)

               //{

               //    screenBuffer.Dispose();

               screenBuffer = null;

               //}

               screenBuffer = new Bitmap(1600, 480);

           }

           private void threadStart_Click(object sender, EventArgs e)

           {

               ThreadStart ts = new ThreadStart(test);

               Thread t = new Thread(ts);

               t.Start();

               ThreadStart tsalloc = new ThreadStart(alloc);

               Thread talloc = new Thread(tsalloc);

               talloc.Start();

           }

           void test()

           {

               for (int i = 0; i < 1000000000; i++)

               {

                   this.Invoke(new SetText(settext));

                   System.Threading.Thread.Sleep(1000);

               }

           }

           void alloc()

           {

               for (int i = 0; i < 1000000000; i++)

               {

                   this.Invoke(new EventHandler(eatMem_Click));

                   System.Threading.Thread.Sleep(1000);

               }

           }

           static int ctr = 1;

           void settext()

           {

               label1.Text = ctr.ToString();

               ctr++;

           }

    exe code

    using memmoryTestDll;

    namespace memmoryTest

    {

       static class Program

       {

           /// <summary>

           /// The main entry point for the application.

           /// </summary>

           [MTAThread]

           static void Main()

           {

               memmoryTestDll.OptimizedDLL.StartUp.Main();

           }

       }

    }

    I  did not see any difference , am i missing something?

    the application runs of memory after allocating around 38 bitmaps

  25. achellies says:

    Do this tech available for the native exes and dlls, i want to use the webkit under windows CE 5.0, but th webkit's image is so large, so i want to use the technology mentioned in this article, do it available for the native exes and dlls?

  26. Hi.

    I created two project. The first,I never converted to dll, I leave it in "windows application" and the second is the MainApp empty.

    But, I'm same to Sesham, I can't see the differences.

    In the first Application, I load image in front windows, after to click a button.

    I repeat this action and the virtual memory is going down, when the memory is under 8M, the application is closed.

    What I doing wrong?

    ____________________________________________________________________________

    Code: Firs Applicartion.

       public class StartUp

       {

           public static void Main()

           {

               Application.Run(new Form1());

           }

       }

    ____________________________________________________________________________

    Code: Second Application MainApp

       static class Program

       {

           /// <summary>

           /// The main entry point for the application.

           /// </summary>

           [MTAThread]

           static void Main()

           {

               MV32M.StartUp.Main();

           }

       }

    ____________________________________________________________________________

  27. me says:

    noob author never answers comments.