A JVM thread has only one program counter register, yet Java methods call other Java methods and must resume correctly on return. Where is the caller's resume point kept during a nested call?
answer
- one pc = current frame only
- caller's resume point lives in its frame
- invoke pushes frame, return restores caller
- every frame prints a line in a stack trace
- depth costs stack, not pc registers
basics
~20 sIn the caller's stack frame. The single PC register always describes the current method only; on invocation the caller's frame retains its own resume position, and on return that frame becomes current again and execution continues after the invoke.
solid answer
~50 sThe pc register is defined as the execution point of the current method, so there is exactly one live value per thread at any moment. Nesting is handled by the JVM stack, not by extra pc registers. When a thread executes an invoke instruction, the JVM pushes a new frame for the callee and makes it current; the caller's frame retains everything needed to continue, including the position to resume from after the call completes. When the callee returns, its frame is discarded, the caller's frame becomes current again, the return value is pushed onto the caller's operand stack, and execution continues at the instruction after the invoke. This is why stack traces show a position and line for every frame, not just the top one: each frame carries its own bytecode index. It is also why deep recursion exhausts the thread stack rather than anything involving the pc register, which never grows.
go deeper
Say that each method call gets its own frame on the thread's stack and the frame remembers where to continue; the pc register only tracks the method running right now.
Walk the invoke and return sequence: frame pushed and made current, callee executes, frame popped, return value onto the caller's operand stack, execution continues after the invoke.
Add that compiled frames hold native return addresses and HotSpot maps them back to bytecode indices via compiler debug metadata, which enables stack traces and deoptimization, including virtual frames for inlined methods.
Use the split to explain resource behaviour: a fixed per-thread execution-point cost versus stack depth cost, which is what actually bounds thread count and recursion depth in a service.
## The apparent contradiction The pc register is one slot per thread, but a thread may be twenty methods deep. If it holds only one address, how does the JVM know where to continue in each of the nineteen suspended callers? ## The resolution: frames own their position The JVM stack holds one frame per active method invocation. A frame contains a local variable array, an operand stack, and a reference to the run-time constant pool of the current class. Frames are created on invocation and destroyed on completion, normal or abrupt. Exactly one frame is current at a time, and the pc register describes the current frame only. The specification's model is: when a method invokes another, the new frame becomes current and the previous frame is left holding everything needed to resume it, including the position at which to continue. The position information is not lost, it is simply not in the pc register while another method is on top. ## What happens on invoke and on return Executing `invokevirtual`, `invokestatic`, `invokeinterface`, `invokespecial`, or `invokedynamic` causes the JVM to pop the arguments from the current operand stack, create a frame for the callee, bind arguments into the callee's local variables, and make the callee's frame current. The pc register now tracks offsets in the callee's bytecode. On a return instruction (`ireturn`, `areturn`, `return`, and so on), the callee's frame is discarded, the caller becomes current again, any return value is pushed onto the caller's operand stack, and execution resumes at the instruction following the invoke. Abrupt completion by an exception is structurally the same: frames are popped until one has a matching handler in its exception table, and the pc register is set to that handler's bytecode offset. ## Evidence you can see A stack trace prints a file and line for every Java frame, not just the innermost. That is only possible because each frame retains its own bytecode index, which is mapped through the method's `LineNumberTable`. If only the pc register carried position information, only the top frame could report a line. ## Implementation reality HotSpot does not literally store a bytecode index in every frame of compiled code. JIT-compiled frames store native return addresses, and the JVM keeps side metadata (debug information emitted with the compiled method) mapping a native address back to a bytecode index for the frames that need it, which is what makes both stack-trace construction and deoptimization possible. Inlining complicates this further: several logical frames may share one physical frame, and the metadata records those virtual frames so the stack trace still shows them. The specification-level model — one pc for the current method plus per-frame resume state — remains the correct mental model. ## Consequences worth stating Because nesting depth costs frames and not pc registers, the resource that runs out on deep recursion is the thread stack (`StackOverflowError`), controlled by `-Xss`, while the pc register is a fixed one-word per-thread cost that never grows. And because the pc register is per-thread rather than per-frame, capturing a thread's state means walking its stack, not just reading its pc.
- When an exception propagates, what determines the new value of the pc register?The JVM searches the current method's exception table for a handler whose range covers the current bytecode index and whose catch type matches. If one is found, the operand stack is cleared, the exception is pushed, and execution continues at the handler's bytecode offset. If not, the frame is popped and the search continues in the caller.
- Why does deep recursion throw StackOverflowError rather than any error involving the pc register?Each invocation allocates a frame on the thread's JVM stack, so depth consumes stack memory bounded by -Xss. The pc register is a single fixed-size slot per thread that does not grow with nesting, so recursion can never exhaust it.
A stack of open books, each with its own bookmark. You read only the top one at a time, but every book below keeps its bookmark so you can continue after closing the one above.
saying these in an interview costs you the question
- Claiming there is one pc register per stack frame
- Saying the return address is pushed onto the operand stack as ordinary data rather than being part of frame state
- Believing only the top frame knows its line number, which contradicts every stack trace
- Assuming HotSpot literally stores a bytecode index in compiled frames rather than reconstructing it from compiler metadata