Inspect memory: returns maximum possible ram usage (away from real code) by design?


I made a short example (ESP32 WEMOS Lolin32, Arduino framework, VSCode, latest PIO):


#pragma once

#include <string>

using std::string;

class RamTest {
string GetString(uint8_t num_);


#include "RamTest.h"

string RamTest::GetString(uint8_t num_) {
    switch (num_) {
        case 0:
            return "bliblablubbliblablubbliblablubbliblablubbliblablub";
        case 1:
            return "ladidaladidaladidaladidaladidaladidaladidaladidalad";
        case 2:
            return "flibedidaflipflapflibedidaflipflapflibedidaflipflap";
        case 3:
            return "bidibidibidibidibidibidibidibidibidibidibidibidib";
        case 4:
            return "naknaknaknaknaknaknaknaknaknaknaknaknaknaknaknaknak";
        case 5:
            return "rollheadoverkeyboardrollheadoverkeyboardrollheadove";
        case 6:
            return "thisisatestthisisatestthisisatestthisisatestthisisa";
        case 7:
            return "boooooringboooooringboooooringboooooringboooooringb";
        case 8:
            return "kappadeltagammacetakappadeltagammacetakappadeltagamc";
        case 9:
            return "thatsenoughthatsenoughthatsenoughthatsenoughthatsen2";
        case 10:
            return "bliblablubbliblablubbliblablubbliblablubbliblablub2";
        case 11:
            return "ladidaladidaladidaladidaladidaladidaladidaladidalad2";
        case 12:
            return "flibedidaflipflapflibedidaflipflapflibedidaflipflap2";
        case 13:
            return "bidibidibidibidibidibidibidibidibidibidibidibidib2";
        case 14:
            return "naknaknaknaknaknaknaknaknaknaknaknaknaknaknaknaknak2";
        case 15:
            return "rollheadoverkeyboardrollheadoverkeyboardrollheadove2";
        case 16:
            return "thisisatestthisisatestthisisatestthisisatestthisisat2";
        case 17:
            return "boooooringboooooringboooooringboooooringboooooringboo2";
        case 18:
            return "kappadeltagammacetakappadeltagammacetakappadeltagamma2";
        case 19:
            return "thatsenoughthatsenoughthatsenoughthatsenoughthatsenou2";
            return "defaultdefaultdefaultdefaultdefaultdefaultdefaultdefaul2";
    return "";


#include <Arduino.h>

#include "RamTest.h"

void setup() {

void loop() {
  RamTest ramTest;

I do not even need to add the RamTest.h in main.cpp and call the method once. As soon as the RamTest - files are available in the src-folder, the memory inspection returns:
112.1 KB RAM (251.2 KB Flash)
if I comment out the switch-statement (only “” is returned), it says:
110,9 KB RAM (246,6 KB Flash)

My problem is the RAM usage:
I have 20 strings (different, same seems not to count) with a size of ~50+ chars = 1000 bytes, which matches the RAM increasing - if all strings are called for, and kept.

In practice, I call for (one of) those strings locally, and they get out of scope shortafter.

When I inspect my projects, and the RAM usage is very high, after ths test I assume they just show me the RAM usage if all returns and all variables are created at once? In reality, there will be much more free RAM available (depending on code, and with some of these methods in it)?
Is this by design?
Or - do I oversee something?

I’ve not looked into this more closely, but this seems not optimal – a std::string object is based on using dynamic memory allocation (heap) and that constant data is better suited to be const char*. The caller can construct a string then through e.g.std::string(RamTest::GetString(0));

You might be running into a sneaky caveat here: The memory inspector tool test-compiles the project in debug mode, which uses the -Og (optimize for debug) compiler flag as opposed to regular building (-Os) (see also build_type for this). Meaning that the sizes in the memory inspector are worst-case measurements of the static memory consumption (it does not count runtime usage of stack or heap).

Have you checked that the firmware size increases in the same way you say in the memory inspector when you just build the project normally? If the compiler is smart with the optimization, adding more return "ajhjkldhfkhl"; strings should only result in an increase in Flash, and 0 in RAM.

This is indeed not the best practice. I use it for many lines of
auto errorMsg = ramTest.GetString(0) + ramTest.GetString(1) + " in " + __PRETTY_FUNCTION__;
cases (its my error string builder), where it just puts the strings together, in contrast to const char* which always needs the surrounding string() then. But yeah, maybe I put it on the refactor-list^^.
By saying constant data I assume you talk about
static constexpr auto ANYCHARARRAY = "ajhjkldhfkhl";
which is then returned?

When building everything is fine. The switch does not add to the RAM size no matter the return amounts, and for the string it just adds 16 bytes once (SSO?), which is not the exact amount, but it also does not “freak out”^^. Even when calling for the same string multiple times, it does not increase.

Thank you for the feedback, now I know when to use the Inspector and how to interpret the results.