First of all there are many JVM implementations, the commercial ones used to offer AOT compilation as well, going back to early 2000.
Then the ones that have JITs offer multiple flavours.
One way is to initially interpret the bytecodes, after enough information it is gathered, the first level JIT gets into action and compiles that block into native code, here block is usually a function, but can be something else.
This first level compiler is rather simple and does only basic optimizations.
The application keeps profiling execution and eventually notices that the already compiled block (into native code) keeps being used significantly, now it is time to bring the big brother JIT, which is somehow equivalent to -O3 on gcc, and recompile again to native code using all major optimizations.
Other JVMs (like JRockit) never interpret, when they start the first level is already the dumb level one compiler to native code.
Then all of them now support JIT caches, meaning after a run, the JITed methods get saved and re-used by next execution, so the profiler gets to learn from previous runs, and execution of the system already starts from a much better performance state.