From my understanding, throw
is a primative jvm command. When this is called, the JVM "checks if the current call stack can catch it". if it can't, then java simply pops the call stack almost exactly as if a return was called. then the jvm "checks if the current call stack can catch it" and so on recursively.
My question: how is it algorithmically possible for the JVM to know where in the call stack can catch a given exception? Is there metadata stored in each call stack entry mapping exceptions to code blocks? is there a static data structure in the heap that somehow keeps track of this? because somewhere there has to be data keeping track of that.
The JVM specification has details about this.
In particular, section 4.7.3 gives details about the exception table, which is a series of entries saying which exceptions are caught between which instructions. Section 3.12 gives a concrete example of this.
How this metadata is mapped into native code for the JIT is a different matter, of course - and implementation-specific. For example, there could be some mapping back from each instruction location in the native JITted code back to the original bytecode location, at which point the exception table can be consulted to find the right handler.