Since you asked for traditional ways, I am going to raise a traditional method. In this case, more than 50 years old.
Generate and analyze a listing.
Compile all the code with debugging turned on (add -g).
Link the code with debugging turned on, producing an ELF executable. Do NOT convert to an image loadable on the arduino.
use objdump to ...
What are the generally practiced methods for the optimization program memory usage?
(nb. as per Edgar's comment I emphasise that this is about using PROGMEM more efficiently.)
If you can replace code with a table whose size is ≤ the lines of code, do it.
Instead of using a sequence of ifs, find a way to collapse the procedure into a table
Use tables of ...
I just want to add a single bullet to Michel Keijzers’ excellent answer:
think about every single item you are storing in memory and ask
yourself the question: do I really need to keep this in RAM?
It may sound silly to state what many would consider obvious, but we
have seen here many instances of novices who do not take this into
consideration. As a ...
What are the generally practiced methods for the optimization program
First, note you are searching for ways to lower SRAM memory. This contains global (variable) memory and heap space (dynamic memory + stack memory).
Avoid memory gaps, by not using dynamic memory (with free/malloc/new).
Avoid using the String class.
Avoid global memory in ...