The JVM


Homework

Do problem 8.1. Due May 7.

Example file

public class HelloWorld
  {
  public static void main( String args[] )
    {
    System.out.println( "Hello, World!" );
    }
  }
Here is the JVM assembly of the Java program:
.class public HelloWorld
.super java/lang/Object

; specify the constructor method for the Example class

.method public <init>()V
  ; just call Object's constructor
  aload_0
  invokespecial java/lang/Object/<init>()V
  return
.end method

; specify the "main" method - this prints "Hello, World!"

.method public static main([Ljava/lang/string;)V
  ; set limits used by this method
  .limit stack 2

  ; Push the output stream and the string "Hello, World!" onto the stack,
  ; then invoke the println method
  getstatic java/lang/System/out Ljava/io/PrintStream;
  ldc "Hello, World!"
  invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V

  return
.end method
Note the init method declaration. This is a constructor call. The ``()V'' means it takes no arguments and returns no values. A different constructor may look like this:
.method public <init>(Ljava/lang/String;)
   ...
.end method
The body of the constructor works like this: The body of the main method works like this:

Architecture

JVM architecture

Constant pool

All the constants in a class and interface are stored in a constant pool, which is an indexed table. The indexes range from 1 to 65535. Each class gets its own constant pool. To refer to a constant in a class, its constant pool index is used.

The constant pool also supports dynamic indirection. The constant pool stores a symbolic reference to the actual constant (as a field/method/class name), and when the constant is needed, the JVM resolves the reference and locates the actual constant. The constant pool can contain: class, field-ref, method-ref, interface-method-ref, string, int, float, double, long.

Instructions

The JVM has over 160 byte-coded instructions. The JVM reads a ``.class'' file one byte at a time. Each byte is a JVM instruction. Some instructions have operands, so these are read from the class file (rather, memory array) at the location or locations immediately following the instruction.

There are a number of execution engines running in each Java application, each is reading and executing bytecodes. Each execution engine is associated with a separate thread.

Instruction prefixes

There are a lot of operations available in the JVM instruction set, but not all operations are available for every basic type. Each operation has a standard instruction name, and for each instruction, there are a number of prefixes, one for each type:
int: i
long: l
float: f
double: d
byte: b
char: c
short: s
reference: a
Some instructions are:
?2d: (i, l, f)
?2l: (i, f, d)
?add; (i, l, f, d)
?aload: (all)
?store: (i, l, f, d, a)

Stack frames

Every time a method is invoked (a function is called), a new stack frame is pushed on the runtime stack, or Java stack. This stores information about the calling procedure. When the method/function/procedure returns, the stack frame is discarded.

Each thread has its own runtime stack. As an aside note, it is important to understand that the stack is a non-mobile data structure. Java is used in mobile agent programming environments, meaning that running Java programs are interrupted and sent from one computer to another, where they resume running. This is only partially supported by Java. Java lets a class store its runtime state in variables, serialize those variables, and restart a program with the transmitted state, but the actual state of a running program (its call stack) can't be moved to another computer.

Registers

The JVM has no general-purpose registers. One reason for this is to allow the JVM to be ported more easily to other platforms.

Operand stack

The object stack is a stack of 32-bit quantities. Longs and doubles (64-bit values) use two entries on the operand stack. Each method call gets a new stack, and the JVM arranges to have the callee return a value to the caller's operand stack.

As with stack entried, local variables are 32-bits wide.

Data operations

There are a large number of instructions for manipulating the operand stack.

Pushing constants:

bipush: push one-byte signed integer
sipush: push 2-byte signed integer
ldc: load single-word constant onto stack
Stack manipulation
nop: do nothing
pop: discard top word on stack
pop2: discard top 2 words on stack
dup: duplicate top word on stack
dup_x1: duplicate top word and insert beneath second word
swap: swap top 2 words on stack
Pushing local variables onto stack:
iload: retrieve integer from local variable
(other prefixes)
Pop stack values into local variables:
istore: store integer from stack into local variable
Flow control:
ifeq LABEL: branch if TOS = 0
ifnull LABEL: branch if TOS = null
(etc.)
goto LABEL: jump to label
jsr LABEL: jump to subroutine
ret LOCVAR: return from subroutine, store TOS in local variable number LOCVAR