What are the main parts of the JVM, and what does each one do?
The JVM has four large pieces. The class loader subsystem loads, links and initialises classes. The runtime data areas are the memory the JVM manages while the program runs. The execution engine interprets and compiles the bytecode. And the runtime library provides the class library the program actually calls into.
For an interview the runtime data areas are the part worth being precise about, because they are what people actually get asked about. The heap holds every object and is shared between all threads; this is where the garbage collector works. The method area holds the metadata for each class — its field and method definitions, the constant pool, and the bytecode — and is part of the heap in modern implementations. The JVM stack is created per thread and holds a frame for each active method, with the frames holding the local variables and the operands of the expression being evaluated. The program counter is a per-thread register pointing at the instruction currently being executed. And the native method stack holds frames for the native code the JVM calls into.
Two details that separate the knowledgeable from the rote-learners. Each thread has its own stack and its own program counter, which is why one thread blocking does not affect another. And the stack is not heap memory — stack frames are allocated and reclaimed in a strictly last-in-first-out fashion, which is why a deep recursion overflows it quickly while allocating a large array does not.
Here is the same thing as runnable code:
public class Main {
public static void main(String[] args) {
// Ask the JVM about itself.
System.out.println("java.vm.name : " + System.getProperty("java.vm.name"));
System.out.println("java.vm.version : " + System.getProperty("java.vm.version"));
System.out.println("java.vm.vendor : " + System.getProperty("java.vm.vendor"));
System.out.println("java.home : " + System.getProperty("java.home"));
System.out.println("spec version : " + System.getProperty("java.specification.version"));
System.out.println("runtime version : " + Runtime.version());
System.out.println("available CPUs : " + Runtime.getRuntime().availableProcessors());
System.out.println("max memory : " + Runtime.getRuntime().maxMemory() / (1024 * 1024) + " MB");
}
}