I actively avoid dynamic allocation in my embedded firmware, writing custom dumbed down static replacements for some newlib APIs that can call malloc.
You'd be amazed at how much stuff (printf, etc) pulls in a dynamic allocator.
I usually use my own stripped down printf that is interface-compatible for a strict subset of the most common format specifiers (decimal/hex integer, strings, very basic padding)