- A class file consists of bytecode and a symbol table
- There are two kinds of types: primitives and references
- Reference types can be either a dynamically allocated class instance or an array.
- There is no way to distinguish primitive types within bytecode except for the operands used to
manipulate them. Each operand has a different version depending on the type: e.g.:
iadd
, ladd
,
fadd
and dadd
are addition operands for int
, long
, float
and double
.
- Integral, signed numbers use two’s-component notation
- Primitive types memory:
byte
, 8-bit, from 2^7
to 2^7 - 1
short
, 16-bit, from 2^15
to 2^15 - 1
int
, 32-bit, from 2^31
to 2^31 - 1
long
, 64-bit, from 2^65
to 2^65 - 1
char
is the only integral type that is unsigned and it represents a Unicode code point encoded in UTF-16
- There is a primitive type called
returnAddress
, whose value can be pointers to the opcodes of JVM instructions.
- The
pc
(program counter) register contains the address of the current JVM instruction being executed.
There is one pc
per thread.
- Every thread contains a stack (created when the thread itself is created)
- Every stack stores frames
- There is a limited to the number of frames per stack, that may be tunable depending on the JVM implementation.
- If the limit of frames is reached, an
StackOverflow
exception is thrown.
- When a new thread is created but there is no enough memory to create its initial stack,
an
OutOfMemoryError
is thrown.
- Heap is the runtime data area where objects are allocated. It is shared by all threads and
is created when the JVM is started up.
- If a computation requires more heap than available
OutOfMemoryError
is thrown.
- The method area is where the runtime constant pool, fields information and functions’ code is stored,
in a per-class basis. Shared by all threads.
- The runtime constant pool is a per-class representation of the class’ constant_pool table.
- Allocated in the method area.
- A frame stores partial results, performs dynamic linking, returns values from methods and dispatch exceptions.
- Created everytime a method is invoked, destroyed when the method terminates or throws.
- Local variables is an array of the variables of a frame (method)
- Size determined at compile time
- Can hold boolean, int, byte, char, short, float, reference and returnAddress.
- long and double are represented by two local variables
- Referenced by index
- Method parameters are assigned to local variables
- For class methods, starting at 0
- For isntance methods, starting at 1 (0 would be a reference to
this
)
- Operand stack is a LIFO stack where operands are pushed or pop by bytecode instructions.
- Constructors are named
<init>
in the bytecode
- Class initializers are named
<clinit>
.
- When an exception is thrown and is not handled in the current method, local variables and operand
stack are discarded, the current frame is poped from the stack, and the exception rethrown in
the context of the invoker’s frame.
- If the exception propagates unhandled after reaching the last frame, its thread is terminated.
- JVM instructions are one byte opcodes
- Ignorning exceptions, the inner loop of a JVM looks like this:
do {
atomically calc pc and fetch opcode at pc;
if (operands)
fetch operands;
execute action for opcode;
} while( there is more to do);
- If operands are bigger than one byte, they are saved in big-endian order.
- Internally, the JVM does not handle
booleans
. Instead, the compiler emits instructions
that operate on int
s.
load
and store
instructions push and pop data into and from the stack. The parameter for
the instruction is the index of a local variable. e.g. iload 3
pushes the 4th local variable
in the frame to the stack.
- Comparisons between
long
, float
and double
yield an integer, which is then tested using
one of the int
-specific instructions and the branching is performed.