Недостаточно памяти, программа может работать нестабильно

Ардуино УНО, ATMEGA328PU
Компилятор выдает такое сообщение:
Скетч использует 13522 байт (41%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 1583 байт (77%) динамической памяти, оставляя 465 байт для локальных переменных. Максимум: 2048 байт.
Недостаточно памяти, программа может работать нестабильно.

Вопрос: На основе чего компилятор решает, что недостаточно памяти?
Это какой-то серьезный анализ кода, в котором производится вычисление максимальной глубины использования стека и прочей необходимой памяти или просто тупое сообщение о том что свободной памяти осталось менее определенного процента?

Флеша свободного дофига, может стоит что-то в прогмем засунуть?

Ругаться начинает, если остаток для локальных переменных меньше 512 байт…

Банально делит всю доступную память на 4.
Если остается более 1/4 - все нормально, а если менее - выдает процитированное предупреждение.

Экранный буфер?

При 512 молчит - при 511 ругается … для 328

Понятно. Т.е. получается что это абсолютно бесполезное предупреждение.

Экранный буфер? Точно?

Если программист точно знает, что он делает и зачем, любое предупреждение для него будет бесполезным. А если программист признает за собой возможность ошибки, то любое предупреждение может (но не обязано) оказаться полезным.

Ну вообще-то у меня есть экранный буфер размером 640 байт, ну и кроме того еще используется библиотека для работы с SD-картой. Все остальное там по мелочи.
Поначалу думал что если это сообщение о нехватке памяти серьезное - то переделаю вывод на дисплей и сокращу экранный буфер до чуть более 100 байт. (ну т.е. сделав его не графическим, а символьным). Но так делать не хотелось бы по той причине, что текст уже не получится вывести куда угодно, а только по знакоместам, ну и кроме того не получится использовать пропорциональный шрифт. Но судя по ответам решил пока не заморачиваться уменьшением буфера.

А вообще как можно приблизительно оценить РЕАЛЬНОЕ наименьшее возможное кол-во свободной памяти?
Например можно ли в произвольный момент узнать адрес, где начинается свободная память?

Вычислить разницу между текущим значением стека и кучи в том месте, где предполагается нехватка памяти. Ну и при выделении памяти делать проверки.

Можно (хотя это и зависит от того, что Вы подразумеваете под “свободной памятью”).

Все верно, именно это я и имею ввиду.
Т.е. к примеру нахожу разницу между вершиной стека и кучей. Это будет размером свободной памяти. Сравниваю это значение с сохраненным, если оно меньше, то заменяю сохраненное. Полученную подпрограмму вешаю на прерывание по таймеру, настраиваю таймер, чтобы он срабатывал как можно чаще, но в то же время оставлял возможность работы основной программе.
Ну и через какое-то значительное время считываю получившееся значение и вывожу куда-нить.
Только как узнать адрес где заканчивается куча?

inline uint32_t getRamFree(void) {
  extern uint16_t __heap_start, *__brkval;
  uint16_t v;
  return (uint32_t) &v - (__brkval == 0 ? (uint32_t) &__heap_start : (uint32_t) __brkval);
}

На разных платформах определены разные имена, поэтому наиболее универсальный метод - запросить память в куче (запомнив адрес), после чего освободить ее.

Это точно не всегда сработает. Менеджер кучи (если он есть) - может выдать память из дыр внутри кучи …

Ну, во-первых, можно попытаться сделать так, чтобы было всегда. Например, сделать несколько итераций с разным объемом запрашиваемой памяти.
А во-вторых, это в большей степени зависит от характера работы с памятью: можно ведь и просто не допускать фрагментирования памяти.
Ну и никогда не следует забывать, что Arduino - это не только AVR.
Кстати, а что вообще выдает __heap_start/__heap_end при фрагменитированной памяти?

malloc и так проверяет будет ли затирание стека и возвращает 0, если требуемый кусок уже пересекает нижнюю границу стека…
А если “писатель” устроил рекурсию или просто организовал в стеке большой массив и затер верхушку кучи, то тут уже ничего не поможет…

Вот именно к этому и относится моя фраза:

Кстати, а что именно хранится в __brkval и __heap_start ?

https://www.google.com/search?q=__brkval