JVM Organization

Think of the JVM as a virtual processor connected to three memory areas:

Class Area

All Java methods are compiled into Bytecodes which are stored in this area.

Heap

Every time new is invoked, memory for an instance of a class is allocated in this area.

When the heap runs low, objects no longer in use get automatically recycled by a program called the garbage collector (gc).

Java Stack

Every time a method is invoked a stack frame is created and pushed onto the Java Stack. When the method terminates, this frame is popped off the stack. Thus, the top-most frame of the Java stack always belongs to the method that is currently being executed by the JVM.

Stack Frames

The format of a stack frame is important to understand. There are three areas contained in the stack frame:

The control array contains a reference to the current method (in the class area), a reference to the calling frame (which is the frame below) and a program counter (PC) pointing to the next Bytecode instruction of the current method.

The locals array contains all local variables and parameters of the currently executing method.

There are two types of processors: register machines and stack machines. Register machines store intermediate results of lengthy calculations in registers. Stack machines store these results on a stack. The JVM is a stack machine. Each method gets its own operands stack where it can store the intermediate values of a calculation.

Example

For example, consider the calculation:

a = x + y * z

To do this calculation a stack machine would first push x, y, and z on the stack:

push x
push y
push z

Next it would replace the top two items on the stack—y and z—by their product:

mul

Next, it replaces the top two elements—x and y * z—by their sum:

add

Finally, it stores the top element into variable a:

pop a

Example

Assume the following Java class declarations have been made:

class Car {
   double speed, gas;
   void start(double batteryCharge) { ... }
   void drive(...) {
      start(x);
      // etc
   }
   // etc.    
}

class TestCars {
   public static void main(String[] args) {
      Car c1 = new Car();
      Car c2 = new Car();
      c1.drive();
      // etc.
   }
}

Here is a snapshot of memory at the instant c1.drive() calls c1.start():

 

Note that in start's stack frame:

locals[0] = this = c1 = the object executing the start method
locals[1] = batteryCharge = x

Complications

There are two complications in the above description.

First, Java is a multi-threaded language. A thread is an object that has its own virtual machine and its own Java stack. When asked to execute a method, a thread uses its own virtual machine and Java stack. Thus, there can be several JVMs and several Java stacks coexisting at the same time.

Second, Java has the ability to call native methods. A native method is a function from another language which is executed by the CPU of the host machine. The locals for these functions need to be kept in a separate native methods stack

 

 

 

 

 

.