Memories of a Java Runtime
It’s been a while since I wrote anything about Java Virtual Machine (JVM). I have been using Java for quite some time, yet my understanding of the inner workings of JVM seems to fade if I don’t refresh it once in a while. This posting is an attempt to renew my memories while trying to add something new. I plan to post a series of articles exploring different aspects of JVM.
The Java memory seems to be the part which trips most folks. What better way to start than exploring the JVM’s runtime memory? The JVM runtime memory can be broadly classified into two groups: common memory areas and exclusive areas. The common memory areas are created during JVM startup and shared across all threads. They are destroyed during JVM exit.
Exclusive areas are created during new thread instantiations. A thread specific area is only accessible by the thread responsible for its creation. It’s destroyed during the thread exits. Figure 1 shows different groups of JVM runtime memory.
The method area and heap falls under common memory areas while JVM stack, stack frame, operand stack, native stack, local variables, and program counter (PC) register are grouped under thread specific memory areas.
Common Memory Areas
Once a class bytecode is loaded by a JVM class loader, it’s passed to the JVM for further processing. The JVM creates an internal representation of the class and stores it in the method area. An example of a class method area is shown in Figure 2. The following data areas are contained within the internal representation of a class:
Runtime Constant Pool contains constants used in a particular class. The constants can be of types int, float, double, and UTF-8. It also contains references to methods and fields.
Method Code is the implementation (opcodes) of all class methods.
Attribute and Field Values contain all named attributes and field values of a class. A field value points to a value stored in the runtime constant pool.
A method area can be part of a heap. It’s created during runtime and available to all threads. Its size can be fixed or changed dynamically. Providing a garbage collection mechanism for the method area is optional. A JVM throws a OutofMemoryError if the method area runs out of allocation space.
A heap, shown in Figure 3, is created during JVM startup time and used for storing instances of classes and arrays. The size of a heap can be fixed or dynamic. A heap must provide garbage collection mechanism to reclaim unused space. However, a garbage collection implementation scheme is not specified and it is configurable. A heap area is shared across all threads. A JVM throws an OutofMemoryError if the heap runs out of allocation space.
Exclusive Memory Areas
Every thread has a private JVM stack. A stack is created during a thread startup time and its size can be static or dynamic. A JVM stack is used for storing stack frames as shown in Figure 4. A new stack frame is created and pushed into a thread stack every time a method is invoked. A frame is popped when a method returns. Though there may be multiple frames on a stack from nested method calls, only one frame is active at a given time for a thread.
A JVM throws a StackOverflowError when a thread needs a stack area larger than permitted or memory available. If a JVM stack is dynamically allocated, a JVM may throw an OutofMemoryError if insufficient memory is available to meet stack size increase request. It may also throw a OutofMemoryError if insufficient memory is available during initial stack allocation.
A new stack frame is allocated and pushed into a JVM stack every time a method is invoked. A frame is popped from the stack and destroyed upon the completion of method invocation irrespective of whether the completion is normal or abrupt (uncaught exception). As shown in Figure 5, each frame has its own operand stack, an array of local variables, and a reference to the runtime constant pool.
Local Variables: A JVM uses local variables to pass around method parameters. Each frame contains an array of local variables. The size of the table is determined at class compile time. It can store values of type boolean, byte, char, short, int, float, reference, or return address. A long or a double value occupies two consecutive local variables.
For all instance methods including constructors, the local variable at index 0 always refers to this object. Subsequent indexes, starting at position 1, stores method parameters.
Operand Stack: Each frame contains an operand stack and the stack’s maximum depth is determined at compile time. An operand stack consists of JVM instructions (opcodes) to load local or field variables. The JVM may also instruct to take operands from the operand stack, operate on them, and push the result back on the operand stack. An operand stack is used for preparing parameters required to invoke a method. It’s also used for receiving the result back from an invoked method.
Reference to Runtime Constant Pool: A reference to the runtime constant pool of the method’s class which is stored in the method area.
Each thread has its own pc (program counter) register. If the method is nonnative, a pc register points to the address of the JVM instruction currently being executed by the thread. The content of a PC register is not defined for a native method.
Native Method Stack
A native method gets its own stack called C-Stack.
In my future postings, I plan to address Java memory model and its management.