-
Class Loading Check
-
Triggering Class Loading:
If the target class has not yet been loaded, the JVM initiates the class loading process using a ClassLoader. This process typically involves the following phases:
- Loading: Reads the bytecode from a file or network and converts it into JVM-internal data structures.
- Verification: Ensures the bytecode complies with the JVM specifications to maintain security and integrity.
- Preparation: Allocates memory for class variables and sets their default initial values.
- Resolution: Converts symbolic references in the class into direct references. (Note that resolution may be delayed until the first time the class is used.)
-
Initialization: Executes the class constructor (
<clinit>
) to initialize static variables and execute static code blocks.
- After these steps, the class metadata (methods, fields, inheritance details, etc.) is loaded into the Method Area (or Metaspace), ensuring that the JVM can correctly refer to the class information during object creation.
-
Triggering Class Loading:
If the target class has not yet been loaded, the JVM initiates the class loading process using a ClassLoader. This process typically involves the following phases:
-
Memory Allocation
-
Allocation Strategies:
The JVM allocates memory for the new object on the heap using different strategies depending on the state of the memory:
- Bump-the-Pointer: This strategy is used when the heap is contiguous and well-organized. A pointer is simply moved to allocate a continuous block of memory.
- Free List: When the heap is fragmented, the JVM maintains a free list—a list of available memory blocks—and searches for a block that is large enough for the new object.
-
Concurrent Optimization:
- CAS (Compare and Swap): In multi-threaded environments, CAS operations ensure that updates to the heap pointer are atomic. This prevents race conditions by allowing only one thread to successfully update the pointer at a time.
- TLAB (Thread-Local Allocation Buffer): Each thread is provided with its own private memory buffer. This reduces contention between threads when allocating memory. However, if an object is too large (e.g., a large array), it might bypass the TLAB and be allocated directly in the shared heap space (such as the old generation).
-
Allocation Strategies:
The JVM allocates memory for the new object on the heap using different strategies depending on the state of the memory:
-
Memory Space Initialization (Initialization to Zero Values)
- Once memory is allocated, the JVM initializes the entire memory region to zero (default values). This step is critical to ensure that all fields of the object start with a known state:
-
Primitive Types:
For example,
int
is set to 0,boolean
to false,long
to 0L,float
anddouble
to 0.0, andchar
to'\u0000'
. -
Reference Types:
All reference variables are initialized to
null
.
-
Primitive Types:
For example,
- This initialization prevents the object from containing random or undefined values before the explicit initialization code runs.
- Once memory is allocated, the JVM initializes the entire memory region to zero (default values). This step is critical to ensure that all fields of the object start with a known state:
-
Object Header Setup
-
Structure of the Object Header:
The object header typically consists of two main components:
-
Mark Word:
This field stores runtime data about the object, such as its lock state (e.g., no lock, biased lock, lightweight lock, heavyweight lock), GC generation age, and a lazily computed hashcode (if
hashCode()
is invoked). - Klass Pointer: This pointer references the class metadata in the Method Area, enabling the JVM to determine the object's type.
-
Mark Word:
This field stores runtime data about the object, such as its lock state (e.g., no lock, biased lock, lightweight lock, heavyweight lock), GC generation age, and a lazily computed hashcode (if
- Special Considerations: For array objects, the header also includes the length of the array.
-
Structure of the Object Header:
The object header typically consists of two main components:
-
Executing the Instance Initialization Method (
<init>
Method)-
Constructor Invocation:
After setting up the memory and object header, the JVM calls the instance initialization method (the constructor, denoted by
<init>
) of the object. -
Initialization Process:
- Default Initialization: The fields are already set to their default (zero) values.
- Explicit Field Initialization: The JVM then applies any explicit field assignments provided in the class definition.
- Initialization Blocks: Any non-static initialization blocks are executed.
- Constructor Body: Finally, the code inside the constructor is executed to perform any additional setup required by the program.
- Inheritance Handling: The initialization follows the order dictated by inheritance: the constructor of the parent class is executed first, followed by the child's constructor.
- Once the
<init>
method completes, the object is fully initialized and ready for use.
-
Constructor Invocation:
After setting up the memory and object header, the JVM calls the instance initialization method (the constructor, denoted by
Conclusion:
When you invoke the new
keyword in Java, the JVM executes the following sequence:
- Class Loading Check: Loads, verifies, prepares, resolves, and initializes the class if it isn’t already loaded.
- Memory Allocation: Allocates heap memory using strategies such as bump-the-pointer or free list, optimized for multi-threaded environments using CAS and TLAB.
- Memory Initialization: Initializes the allocated memory to default zero values.
- Object Header Setup: Configures the object's header with the Mark Word and Klass Pointer.
-
Instance Initialization: Executes the
<init>
method, following the proper initialization order, including parent class initialization.
-
类加载检查(Class Loading Check)
-
触发类加载:
如果目标类尚未被加载,JVM 会通过类加载器(ClassLoader)启动类加载过程。这个过程通常包含以下阶段:
- 加载(Loading):从文件或网络中读取字节码,并转换为 JVM 内部数据结构。
- 验证(Verification):检查字节码是否符合 JVM 规范,确保安全性。
- 准备(Preparation):为类变量分配内存,并设置默认初始值。
- 解析(Resolution):将类中的符号引用转换为直接引用(这一步有时会延迟到首次使用时进行)。
-
初始化(Initialization):执行类构造器
<clinit>
方法,初始化静态变量和静态代码块。
- 经过上述过程,类的元数据(如方法、字段、继承关系等)会加载到方法区(或 Metaspace),确保后续对象创建能正确引用该类的信息。
-
触发类加载:
如果目标类尚未被加载,JVM 会通过类加载器(ClassLoader)启动类加载过程。这个过程通常包含以下阶段:
-
内存分配(Memory Allocation)
-
分配策略:
JVM 根据当前堆内存状况采用不同的内存分配方式:
- 指针碰撞(Bump-the-Pointer):适用于堆内存连续且规整的情况,直接通过移动指针来分配连续内存块。
- 空闲列表(Free List):当堆内存存在碎片化时,维护一张空闲内存块列表,在分配时查找足够大的内存块。
-
多线程并发优化:
- CAS(Compare and Swap)操作: 在多线程环境下,为确保内存分配过程中对堆指针的更新操作具有原子性和线程安全性,JVM 可能采用 CAS 原子操作。 例如,当多个线程同时试图分配内存时,CAS 操作可以保证只有一个线程成功更新堆指针,从而防止竞争条件和数据不一致的问题。
- TLAB(Thread-Local Allocation Buffer): 每个线程分配一个私有的内存缓冲区(TLAB),线程在自己的 TLAB 中快速分配内存,降低多线程竞争带来的性能开销。 对于较大的对象(如大数组),可能会绕过 TLAB,直接在堆的公共区域(如老年代)中分配。
-
分配策略:
JVM 根据当前堆内存状况采用不同的内存分配方式:
-
内存空间初始化(Initialization to Zero Values)
- 内存分配完成后,JVM 会将分配到的内存空间初始化为零值(默认值),以确保对象各字段在未显式赋值时不会包含未定义或随机的值。
-
基本数据类型:
-
int
初始化为 0 -
boolean
初始化为 false -
long
初始化为 0L -
float
和double
初始化为 0.0 -
char
初始化为'\u0000'
-
-
引用类型:初始化为
null
。
-
设置对象头(Object Header Setup)
-
对象头结构:
对象头主要包含两部分信息:
- Mark Word:存储对象的运行时数据,如锁状态(无锁、偏向锁、轻量级锁、重量级锁)、GC 分代年龄等,以及在需要时延迟计算的哈希码(hashcode)。
- Klass Pointer:指向方法区中该类的元数据,确定对象的类型。
- 特殊情况: 对于数组对象,对象头中还会包含数组的长度信息。
-
对象头结构:
对象头主要包含两部分信息:
-
执行实例初始化方法( 方法)
-
调用构造方法:
完成内存分配和对象头设置后,JVM 会调用该对象的实例初始化方法(构造函数
<init>
)。 -
初始化过程:
- 默认初始化:前面已将所有字段初始化为零值。
- 显式赋值:根据代码中对实例变量的显式初始化赋值。
- 初始化块:执行非静态代码块中的初始化逻辑。
- 构造方法体:执行构造函数中定义的具体逻辑。
- 继承关系处理:构造过程遵循“先初始化父类,再初始化子类”的顺序,确保继承体系中各层次的正确初始化。
- 当
<init>
方法执行完毕后,对象进入可用状态,并能被程序后续逻辑使用。
-
调用构造方法:
完成内存分配和对象头设置后,JVM 会调用该对象的实例初始化方法(构造函数
总结:
JVM 在通过 new
关键字创建新对象时,依次执行以下步骤:
- 类加载检查(如果类未加载,则完成加载、验证、准备、解析和初始化过程),
- 内存分配(采用指针碰撞或空闲列表策略,在多线程环境下通过 CAS 操作和 TLAB 优化分配),
- 内存空间初始化(初始化为零值以确保内存安全),
- 设置对象头(设置 Mark Word 和 Klass Pointer),
- 执行实例初始化方法(按继承顺序执行初始化块和构造方法)。
Top comments (0)