Main Site Documentation

Base Memory Usage of Selected .NETMF Namespaces - Fez Raptor


#1

Hi all, I just trying to understand what the memory using would be of the below namespaces assuming I haven’t added any of my own custom code, I’m using a Fez Raptor, I’ve tried using “int memAvailable = Debug.GC(true);” but I’m not sure of the accuracy of this.

using System;
using System.Collections;
using System.Math;
using System.Threading;
using Microsoft.SPOT.Hardware;
using Gadgeteer;
using Gadgeteer.Modules;


#2

@ chillydk147 - The exact memory usage depends on which classes in those namespaces you use. Debug.GC(true) will return a reliable amount of available memory.


#3

Which type of memory are you asking about? There is flash memory where the program is loaded, and RAM which is used by your executing program.

What John said will tell you the amount of RAM available.

I can not recall when anyone has actually run out of flash memory. There is a 700K limitation on the size of an assembly, but you can split your program into multiple assemblies (DLLs).

Raptor has lots of both memory types.


#4

Why do you need to know? There is so much ram on raptor that you can almost assume it is unlimited.


#5

I have exactly the same question except mine is for Cerbuino which has real memory constraints.

I actually ran out of memory, so I switched to my Raptor to see what was going on. And it is using a ton of memory (509K) even before my code starts. Not many modules attached: ENC28, SD, USB with power, couple of headers connected to sockets 4 (using i2c) and 18 (using as a Y socket). On the Raptor the rest of the code runs fine as there is almost infinite memory.

Is there a way to understand what is using the RAM before our code starts and to configure it in any way if I am trying to squeeze into a smaller RAM?

        void ProgramStarted()
        {
            Debug.Print("Program Started");
            Debug.Print(Debug.GC(true).ToString());

The debug output is:

[quote]
Using mainboard GHI Electronics FEZ Raptor version 1.0
Program Started
GC: 1msec 509184 bytes used, 66596580 bytes available
Type 0F (STRING ): 1548 bytes
Type 11 (CLASS ): 17568 bytes
Type 12 (VALUETYPE ): 1608 bytes
Type 13 (SZARRAY ): 7236 bytes
Type 01 (BOOLEAN ): 24 bytes
Type 03 (U1 ): 1020 bytes
Type 04 (CHAR ): 1188 bytes
Type 07 (I4 ): 1332 bytes
Type 0F (STRING ): 96 bytes
Type 11 (CLASS ): 3492 bytes
Type 12 (VALUETYPE ): 84 bytes
Type 15 (FREEBLOCK ): 66596580 bytes
Type 16 (CACHEDBLOCK ): 72 bytes
Type 17 (ASSEMBLY ): 50460 bytes
Type 18 (WEAKCLASS ): 96 bytes
Type 19 (REFLECTION ): 168 bytes
Type 1B (DELEGATE_HEAD ): 936 bytes
Type 1C (DELEGATELIST_HEAD ): 48 bytes
Type 1D (OBJECT_TO_EVENT ): 312 bytes
Type 1E (BINARY_BLOB_HEAD ): 419640 bytes
Type 1F (THREAD ): 1536 bytes
Type 20 (SUBTHREAD ): 192 bytes
Type 21 (STACK_FRAME ): 1224 bytes
Type 22 (TIMER_HEAD ): 144 bytes
Type 26 (WAIT_FOR_OBJECT_HEAD): 48 bytes
Type 27 (FINALIZER_HEAD ): 192 bytes
Type 31 (IO_PORT ): 180 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 5904 bytes
GC: performing heap compaction…
66596580 [/quote]


#6

pretty sure the answer will always be - whatever your code is doing is what is consuming memory. :wink:

@ Duncan, whatis the problem with 66mb free? Doesn’t seem to be decreasing ??


#7

Hi guys, thanks for all the replies, I perhaps should have clarified my situation, Im new to embedded programming but not new to c# hence why I’ve choosen .netmf. I decided to purchase a fez raptor as I was sure it would more than suit my needs, now that ive finalised my code and see that the code consumes no more than 40kb, I now want to move onto a smaller board, fez cerberus to be precise, so I was hoping to know based on my codes memory consumption and the .net assemblies mentioned above, would my project be a comfortable fit on fez cerberus?


#8

Also my total program memory which gets flashed is 120kb


#9

your RAM usage is what will be a bigger constraint on a Cerb. That is going to be unrelated to code size. That’s the risk you face when just trying to shrink to a different device. To help you further you’re going to have to tell us about what you do or show us code. From my previous interactions, @ Taylorza and @ Architect are the gurus when helping to understand this kind of thing, but debug.gc is going to be the first tool you can use, looking at how your memory pattern changes as classes are used and as you progress your app.


#10

My project uses the assemblies mentioned, my code uses

Arraylists - these are a fixed capacity,
Hashtables (these are static readonly so not sure whether they affect RAM),
bytes and shorts - these are used to populate the Arraylists

As I said my code never uses more that 40KB at any time, I’m using an initial calculation of memory available at program start as a reference to calculate my code ram usage.


#11

so what’s your question ?

Are you interested in code size (flash usage) or ram usage (objects/classes/variables)


#12

Ram usage is what I’m interested in as I’m finding it a little confusing as to what Ram my assemblies are using, as I stated my code never exceeds 40KB, so with Fez Cerb that would in theory leave 64KB to play with, my original question was how much Ram is being used by declaring these assemblies?


#13

none is used by declaring “using” statements.


#14

@ chillydk147 - yes you will find it confusing because it is very dynamic and will change every second your program run. You can see the free memory at any time but it will always change depending on what is going on. This is a great benefit of a modern programming language, memory management.


#15

Ok, so what im understanding here is that including an assembly via a using statement, if no classes of that assembly are ever used then that assembly will only have an effect total flash usage and never on ram usage


#16

not quite… but close. A USING statement is only a shortcut to a namespace, so it will have no real change in data or code, irrespective of what you do.

Adding a reference to a new namespace will include the assembly which will change your flash usage, whether you create anything from it or not. When you use the class of that assembly you’ll consume RAM.


#17

I don’t think we are getting our question answered.

There seems to be an understanding that we are asking about memory during the running of the program. In my case at least I completely understand dynamic memory and memory managers, etc. I have designed and written them. RAM memory, not the flash. I understand that every time I create an instance of anything some RAM is used and that when I dispose of the instance the RAM is freed for use elsewhere. I think the answers we are getting is explaining this.

My question( and I think chillydl147’s ) is about only the instant in time when our ProgramStarted routine is called

To clarify my question I created from scratch a new CerbuinoBee Gadgeteer program. I added a print, sleep loop.

I linked an ENC28Ethernet module and two Header modules to the sockets in the diagram tool.

I did not add anything else or change anything. I ran it.

Attached is the source and the output. I am assuming that this number printed out is the memory available. Given that the spec says the CerbuinoBee has > 100k of RAM available, but my print statement indicates that there is only about 40K available when my code starts (and ~5K released a few seconds later), the question is:

Is there any way to find out where the other approximately 60K is going and if there is any way to reduce it.

using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Presentation.Shapes;
using Microsoft.SPOT.Touch;

using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;

namespace Test
{
    public partial class Program
    {
        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
            /*******************************************************************************************
            Modules added in the Program.gadgeteer designer view are used by typing 
            their name followed by a period, e.g.  button.  or  camera.
            
            Many modules generate useful events. Type +=<tab><tab> to add a handler to an event, e.g.:
                button.ButtonPressed +=<tab><tab>
            
            If you want to do something periodically, use a GT.Timer and handle its Tick event, e.g.:
                GT.Timer timer = new GT.Timer(1000); // every second (1000ms)
                timer.Tick +=<tab><tab>
                timer.Start();
            *******************************************************************************************/


            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");
            for (int i = 0; i < 20; i++)
            {
                Debug.Print(Debug.GC(true).ToString());
                Thread.Sleep(10000);
            }
        }
    }
}

And the output (complete with messages about doing too much in the init thread):

[quote]Using mainboard GHI Electronics FEZ Cerbuino Bee version 1.2
Program Started
40176
WARN: Total initialization time exceeds 10 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 20 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 30 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 40 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 50 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 60 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 70 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 80 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 90 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 100 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 110 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 120 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 130 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 140 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 150 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 160 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 170 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 180 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 190 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
45744
WARN: Total initialization time exceeds 200 seconds.
: ProgramStarted is blocking execution, which means events and timers will not run properly.
: Make sure not to use blocking code such as while(true) - use a GT.Timer instead.
The thread ‘’ (0x4) has exited with code 0 (0x0).[/quote]


#18

your code is correct, and the available memory displayed is correct. The Gadgeteer framework loads a heap of stuff - look at the actual main() loop in the program.generated code file and you’ll see a heap of things that the Gadgeteer framework does behind the scenes that you can’t control - except by removing stuff from Gadgeteer. Even if you start from an empty pure netmf project, some memory will be used.

The way you’ve written that code will also give you some unintended results since the Gadgeteer scheduler has to complete to be consistent, and you’re blocking ProgramStarted() from completing. You should set up a timer and do the same and see how different that might be (I predict not very different, but certainly not a sustainable practice)


#19

@ Duncan,

There are a few things going on here that consume various types of memory, to list a few that will reside in the Flash and each will have it’s own RAM requirement

  1. .NETMF VM
  2. .NETMF TinyCLR libraries
  3. .NETMF Gageteer libraries (Optional)
  4. .NETMF Application

Each of the above components will intern allocate some amount of RAM, off the top of my head these are a few things that come to mind without putting too much thought into it, ie. there is more, I am just lazy.

  1. NETMF VM - RAM for it’s internal buffers things like managing linked lists for completion queues, continuation queues, thread context structures etc.

  2. .NETMF TinyCLR Libraries - RAM for any static data the managed and native libraries declare eg. native UART queues

  3. .NETMF Gadgeteer libraries - RAM for event queues (This queue is part of the scheduler @ Brett is referring to that you are blocking with your current test code) and a host of data structures to support the base modules. These libraries are all managed code, so the use of dynamic data structures like ArrayList etc. allocates minimum internal array sizes etc.

  4. .NETMF Application - RAM this is the only RAM allocation that you have some control of, but of course when you use library components you are at the mercy of their memory allocation strategies. All of the above happens before you even get anywhere near here.

Static constructors are all executed even if you do not reference the class in anyway, so all libraries that you might bring in that statically allocate buffers etc. do that allocation before your code even runs.

All the assemblies you reference will get loaded into flash, they are either part of the onboard firmware or they are loaded to the flash as part of the project deployment. Reference here means that you have added a reference to your project. The using directive assumes that you have referenced the relevant assembly that publishes the namespace specified in the using directive otherwise the compile stage will fail.

The point of the above is not to give a detailed rundown of what is using memory where, there is a lot more to be said. I hope it shows that depending on what you are doing and what you reference it is not going to be easy to determine the details of the start-up memory allocations. And then, when you do, you will find things can change with newer firmware versions, right off the bat I can think of one example where the pre-allocated buffers for TCP/UDP handles changed size between 4.2 and 4.3 so the RAM allocated by the firmware changes, and if I recall those are static buffers so they take space whether you use them or not (IIRC). And there are many internal things like that that even GHI would not control, but rely on the core framework.

That last part is not accurate in a managed environment like .NETMF. Disposing of an object makes it available to eventually be freed if and when the GC runs. Releasing of memory, execution of finalizer (destructors) etc. are all non-deterministic in these environments