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

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. This forces the printf and UART drivers to compile and requires 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 yield different memory savings.

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. To save more RAM and flash memory, remove destructor registration on application startup and eliminate the code to destruct objects when the operating system calls exit() at runtime.

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.