JVM Basic
ClassFile
After a Java source file is compiled by javac, it becomes a .class bytecode file and then runs on the JVM. A ClassFile is a structured specification; all .class files must conform to this format.

Basic Info

- magic number: 0xCAFEBABE. Every .class must start with these 4 bytes.
- Version number, representing the compiler (javac) version.
- access flags
Constant Pool
There are 14 kinds of constants, grouped into literals and symbolic references. Literals are strings, final constants, etc. Symbolic references are things like method names and field names; they point to a string entry, i.e., a literal.
javap -v Main.class temp.txt Use this to inspect constant pool information.
Fields, Methods, Attributes
Take methods as an example: the table records access_flags (public/static/volatile), and references into the constant pool (method name string, method descriptor string).
Class Life Cycle
Loading, Linking (Verification, Preparation, Resolution), Initialization, Use, Unloading
Loading
- A class loader obtains the .class bytes via various channels based on the class’s fully qualified name (package + class name). “Various channels” means dynamic proxies can also generate classes, or the class can come from local disk.
- Transform the byte stream into a C++ object (instanceKlass) and place it in the method area in memory. This object contains the class’s metadata.
- Create a java.lang.Class object on the heap (a normal Java object) that represents this class.
Verification
- File format: version number, magic number. The runtime environment must not be older than the class file’s version.
- Metadata: ensure the class has a parent class, i.e., java.lang.Object (except Object itself).
- Bytecode verification: analyze control flow/data flow to ensure semantics are correct.
- Symbolic references: verify that all constant-pool symbolic references are valid and accessible.
Preparation
Assign default values to static variables (numeric types become 0). If a static variable is final, assign its declared value here.
Resolution
Convert symbolic references in the constant pool into direct references, i.e., actual memory addresses for access. This produces the runtime constant pool, stored in the method area.
Initialization
In the ClassFile’s Methods section there’s a default
In this phase, static variables are assigned and static initialization blocks are executed. As shown, push 1 onto the operand stack, then assign it to the static variable in the heap-resident constant pool.
Triggers for class initialization:
- Accessing a class’s static variables/methods (except those marked final).
- Creating an instance with new.
- Reflective operations on the class via java.lang.reflect.
- Running the main method’s declaring class.
Output: DACBCB. “D” prints during class loading; “A” prints in main. When creating Test(), the instance initializer block runs before the constructor. The instance initializer runs on every object creation.
Order of static blocks, instance initializer blocks, and constructors
This prints 2. Running main and new B() first initializes parent A (a = 1), then B (a = 2).
If you remove new B02(), it prints 1 directly (accessing the parent’s static variable does not trigger child initialization).
Class Loaders
ClassLoader is an abstract class. Every Java class holds a reference to the class loader that loaded it.
Categories
Up to Java 8: Bootstrap ClassLoader, Extension ClassLoader, Application ClassLoader.
Bootstrap ClassLoader
Implemented in C/C++; retrieving it in Java yields null. Loads JDK core libraries.
/Users/ruoke/Library/Java/JavaVirtualMachines/corretto-1.8.0_412/Contents/Home/jre/lib
It loads from this directory. You can also specify jars to be loaded with -Xbootclasspath.
Extension ClassLoader
A JDK-provided static inner class implemented in Java. It loads from:
JavaVirtualMachines/corretto-1.8.0_412/Contents/Home/jre/lib/ext
Application ClassLoader
Loads your own classes and jars from the application classpath (e.g., Maven local repository jars, your compiled classes).
Parent Delegation Model
Determines which class loader actually loads a given class.
From bottom to top, check whether a loader has already loaded the class. If yes, return it. If not, delegate upward: the bootstrap loader attempts first; on failure, delegate downward in order.
Advantages
- Ensures core Java classes cannot be tampered with; user-defined classes under package name starting with “java.” won’t override core classes.
- Prevents class duplication. If two different class loaders load classes with the same fully qualified name, they’re considered different types.
Can String be overridden? No. If you define your own String class, the bootstrap loader has already loaded java.lang.String and returns that class.
Breaking Delegation: Custom Class Loaders
In findClass, defineClass(…) turns bytes into the method-area instanceKlass and the heap Class object.
If you want to break parent delegation, override loadClass; if not, just override findClass. The parent of a custom class loader is the ApplicationClassLoader.
Without breaking delegation | Breaking delegation
Only the same class loader plus the same fully qualified name yields the “same” class in Java.
loadClass summary:
- Implements full parent delegation flow
- Calls findLoadedClass to check prior load
- Delegates to parent first
- Calls findClass only after parent fails
Runtime Data Areas
Shared by all threads: Heap, Method Area
Per-thread: JVM Stack, Native Method Stack, Program Counter (PC)
Native Method Stack
In HotSpot, native stack frames are colocated with Java stack frames in the stack memory.
JVM Stack
Stack Frame
- Operand Stack (temporary variables and intermediate results)
- Local Variables: this reference for instance methods, method parameters, and local variables
- Return Address
- Exception Table: covered ranges and handler target bytecode offsets
- Dynamic Linking: during method calls, symbolic references to methods are turned into direct references (addresses)
Stack Overflow
On Linux x86_64 the default per-thread stack size is 1MB. You can adjust with JVM flags. In practice, 256k is often sufficient.
StackOverflowError: if the stack cannot expand and the requested depth (number of frames) exceeds the maximum
OutOfMemoryError: if the stack is expandable but the VM cannot obtain more memory when trying to grow it
Program Counter (PC)
Holds the offset of the bytecode instruction currently being executed. The interpreter uses the method area start address and this offset to compute the instruction’s location.
Heap
All object instances and arrays live on the heap.
Heap memory has three metrics: used, total, max. total means…
[INFO] JAVA_HOME: /Users/ruoke/Library/Java/JavaVirtualMachines/corretto-17.0.13/Contents/Home
[INFO] arthas-boot version: 4.0.4
[INFO] Can not find java process. Try to run jps command lists the instrumented Java HotSpot VMs on the target system.
Please select an available pid.
Start Arthas
memory
Memory used total max usage
heap 667M 1786M 2304M 28.96%
As more objects accumulate, heap used grows and approaches total; the VM then allocates more heap. The VM can expand up to max. When used approaches max you’ll get java.lang.OutOfMemoryError: Java heap space.
By default, max is 1/4 of system memory; total is 1/64. You can adjust via JVM options.
Object allocation happens in Eden. Objects surviving a GC move to S0 or S1 with age 1. By default, objects age to 15 before moving to old generation. You can configure the tenuring threshold (0–15 only).
Default young:old ratio is 1:2. Within young gen, Eden:S0:S1 defaults to 8:1:1.
Method Area
The method area is an abstract concept; PermGen and Metaspace are its concrete implementations.
The method area stores instanceKlass objects:
- Class metadata: class name, parent, method info, field info
- Runtime Constant Pool: entries originating from the ClassFile constant pool; holds literals and references. Numeric literals are stored directly here; String literals live in the heap string pool with only references stored in the runtime pool. Symbolic refs are turned into direct refs during resolution.
- Static variables. These are class-level, not per-instance, so they live in the method area.
- Code: JIT-compiled native code or bytecode for interpretation
JDK 7 and earlier
Method area lived in the heap’s PermGen, which had a fixed upper bound. Initial and max sizes could be configured via flags.

JDK 8+
The method area lives in Metaspace, which uses OS-managed native memory (off-heap).
NIO (New I/O) provides access to direct memory via channels and buffers. DirectByteBuffer objects reference direct memory, enabling faster access than Java heap; good for frequently accessed structures.
