Mistake on this page?
Report an issue in GitHub or email us

Memory

This is a basic overview of the memory model in Mbed OS.

+---------------------+   Last address of RAM
| Scheduler/ISR stack |
+---------------------+
|          ^          |
|          |          |
|                     |
|      Heap cont.     |
|---------------------|
| User thread n stack |
|---------------------|
| User thread 2 stack |
|---------------------|
| User thread 1 stack |
|---------------------|
|          ^          |
|          |          |
|                     |
|        Heap         |
+---------------------+
|                     |
| ZI: Global data     |
|                     |
+---------------------+
| ZI: Idle stack      |
+---------------------+
| ZI: Timer stack     |
+---------------------+
| ZI: Main stack      |
+---------------------+
|                     |
| ZI: Global data     |
|                     |
+---------------------+
| RW: Vector table    |
+=====================+   First address of RAM
|                     |   Last address of flash
|                     |
|     Application     |
|                     |
|                     |
+---------------------+
|                     |
| Optional bootloader |
|                     |
+---------------------+
| RO: Vector table    |
+---------------------+   First address of flash

There are at least two kinds of memory in the system: flash and RAM.

RAM

Inside RAM, you can distinguish two logical types: static and dynamic memory. Static memory is allocated at compile time and, consequently, does not change size during runtime. Dynamic memory is allocated at runtime. For example, the program memory use grows and shrinks as you fork and join threads and construct and destruct objects. The system uses each of them in different ways:

  • Static:
    • Vector table (read and write).
    • Global data.
    • Static data.
    • Stacks for default threads (main, timer, idle and scheduler/ISR).
  • Dynamic:
    • Heap (dynamic data).
    • Stacks for user threads. Mbed OS dynamically allocates memory on heap for user thread's stacks.

Stack checking is turned on for all threads, and the kernel errors if it detects an overflow condition.

Flash

Flash is a read only memory (ROM) that contains:

  • Vector table (read only).
  • Application code.
  • Application data.
  • Optional bootloader.

Static memory optimization

Removing unused modules

For a simple program like Blinky, a program that flashes an LED, typical memory usage is split among the following modules:

+---------------------+-------+-------+-------+
| Module              | .text | .data |  .bss |
+---------------------+-------+-------+-------+
| Fill                |   132 |     4 |  2377 |
| Misc                | 28807 |  2216 |    88 |
| features/frameworks |  4236 |    52 |   744 |
| hal/common          |  2745 |     4 |   325 |
| hal/targets         | 12172 |    12 |   200 |
| rtos/rtos           |   119 |     4 |     0 |
| rtos/rtx            |  5721 |    20 |  6786 |
| Subtotals           | 53932 |  2312 | 10520 |
+---------------------+-------+-------+-------+

The features/frameworks module includes the Mbed OS test tools, even if you are no longer testing your program. Because of this, you are building one of our test harnesses into every binary. Removing this module saves a significant amount of RAM and flash memory.

Printf and UART

The linker can also remove other modules that your program does not use. For example, Blinky's main program doesn't use printf or UART drivers. However, every Mbed OS module handles traces and assertions by redirecting their error messages to printf on serial output - forcing the printf and UART drivers to be compiled in and requiring a large amount of flash memory.

To disable error logging to serial output, set the NDEBUG macro and the following configuration parameter in your program's mbed_app.json file:

{
    "macros": [
        "NDEBUG=1"
    ],
    "target_overrides": {
        "*": {
            "platform.stdio-flush-at-exit": false
        }
    }
}

Note: Different compilers, different results; compiling with one compiler yields different memory usage savings than compiling with another.

Embedded targets

You can also take advantage of the fact that these programs only run on embedded targets. When you run a C++ application on a desktop computer, the operating system constructs every global C++ object before calling main. It also registers a handle to destroy these objects when the program ends. The code the compiler injects has some implications for the application:

  • The code that the compiler injects consumes memory.
  • It implies dynamic memory allocation and thus requires the binary to include malloc, even when the application does not use it.

When you run an application on an embedded device, you don't need handlers to destroy objects when the program exits, because the application will never end. You can save more RAM and flash memory usage by removing destructor registration on application startup and by eliminating the code to destruct objects when the operating system calls exit() at runtime.

Note: Mbed OS 5.2.0+ enables removing destructor registration by default.

Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.