> 技术文档 > 【Java】JVM虚拟机(基本概念、类加载机制)

【Java】JVM虚拟机(基本概念、类加载机制)


一、基本概念

1、什么是JVM

        Java虚拟机(Java Virtual Machine,简称 JVM ),是java程序运行的核心组件之一,它为java程序运行提供了环境。其核心价值在于实现了\" 一次编写,多处运行 \" (Write once,run anywhere )的跨平台特性,还提供了内存管理、垃圾回收、安全性以及性能优化等。

2、JVM的组成

        JVM 的架构可分为加载子系统、运行时数据区、执行引擎、本地方法接口四大核心模块,各模块协同工作完成字节码的加载、执行和资源管理。

【Java】JVM虚拟机(基本概念、类加载机制)

二、类加载机制

1、概述

        Java 的类加载机制是 JVM 将 .class 字节码文件加载到内存中,并对数据进行校验、转换解析、初始化,最终形成可直接使用的 Java 类型的过程。

2、类的生命周期

【Java】JVM虚拟机(基本概念、类加载机制)

3、类的加载过程

        类加载过程主要包括:加载、验证、准备、解析、初始化,五个阶段。

        a、加载

         该阶段主要是通过类的完全限定名获取类的二进制字节流,并生成一个代表该类的Class对象,并将其保存在元空间中。

  •  字节码来源
    • 本地文件系统(.class 文件);
    • 网络(如 Applet 从网络加载);
    • 动态生成(如 JavaCompiler 动态编译、CGLib 动态代理生成字节码);
    • 归档文件(如 JAR、WAR 包);
    • 数据库或其他数据源。

        b、验证

        该阶段主要验证class文件中的字节流包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

        c、准备

        该阶段是在元空间中为静态变量分配内存,并设置其默认初始值(非显示赋值)。

        d、解析

        该阶段只要是将常量池中的符号引用替换为直接引用

        e、初始化

        该阶段为类加载的最后一步,由()编译器自动收集类中所有类变量的赋值和静态代码块中的语句,进行排序并执行。

4、类加载的时机

        在 Java 中,类的加载时机由 JVM 规范严格定义,主要分为主动引用被动引用两种情况。主动引用会触发类的初始化(初始化前的加载、验证、准备阶段会自动触发),而被动引用不会触发初始化。

a、主动引用

  • new() 创建类的实例

        当使用 new 关键字创建类的对象时,类会被初始化。

public class demo01 { public static void main(String[] args) { // 触发 MyClass 初始化 MyClass obj = new MyClass(); }}class MyClass { static { System.out.println(\"MyClass 初始化\"); }}//结果:MyClass 初始化
  • 调用类的静态方法

        当调用类的静态方法(static 方法)时,类会被初始化。

public class demo02 { public static void main(String[] args) { // 触发 Calculator 初始化 int result = Calculator.add(1, 2); }}class Calculator { static { System.out.println(\"Calculator 初始化\"); } public static int add(int a, int b) { return a + b; }}//结果:Calculator 初始化
  • 访问静态变量

        当访问类的静态变量(static 变量)时,类会被初始化。

public class demo03 { public static void main(String[] args) { // 触发 Config 初始化 int port = Config.PORT; System.out.println(\"======================\"); // 不触发 Config 初始化(直接从调用类的常量池获取值) int max = Config.MAX; System.out.println(\"main结束!!!\"); }}class Config { static { System.out.println(\"Config 初始化\"); } public static int PORT = 8080; // 非 final 静态变量 public static final int MAX= 100; // 编译期常量}// 结果:Config 初始化======================main结束!!!
  • 反射调用

        当使用反射newInstance()加载类时,类会被初始化。

public class demo04 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { // 触发 MyClass 初始化 Class cls = MyClass01.class; cls.newInstance(); }}class MyClass01 { static { System.out.println(\"MyClass 初始化\"); }}//结果:MyClass 初始化
  • 初始化子类

        当初始化子类时,若父类尚未初始化,则会先触发父类的初始化。

public class demo05 { public static void main(String[] args) { // 触发 Parent 和 Child 依次初始化 Child child = new Child(); }}class Par { static { System.out.println(\"Parent 初始化\"); }}class Child extends Par { static { System.out.println(\"Child 初始化\"); }}// 结果:Parent 初始化Child 初始化

b、被动引用

除主动引用外,所有引用类的方式都不会触发加载,称为被动引用

  • 创建数组

        创建类的数组时,不会触发类的初始化

public class demo06 { public static void main(String[] args) { data01[] data = new data01[10]; System.out.println(\"main函数结束!\"); }}class data01{ static { System.out.println(\"data01被初始化\"); }}//结果:main函数结束!
  • 通过子类引用父类的静态变量

        子类引用父类的静态变量时,仅触发父类的初始化,子类不会被初始化。

public class demo07 { public static void main(String[] args) { // 仅触发 Parent 初始化,Child 不会初始化 System.out.println(Child.value); // 输出 10 }}class Parent { static { System.out.println(\"Parent 初始化\"); } public static int value = 10;}class Child extends Parent { static { System.out.println(\"Child 初始化\"); }}//结果:Parent 初始化10

5、类加载器

        类加载器是 Java 类加载机制的核心组件,负责将字节码文件加载到 JVM 内存中,并生成对应的 Class 对象。

1、类加载器类型

1. 启动类加载器

  • 加载范围:负责加载 JRE 核心类库(如 rt.jarresources.jar),路径通常为 $JAVA_HOME/jre/lib

2. 扩展类加载

  • 加载范围:负责加载 JRE 扩展目录中的类(如 jre/lib/ext 目录或 java.ext.dirs 系统属性指定的路径)

3. 应用程序类加载器

  • 加载范围:负责加载用户类路径上的类,即开发者编写的类和依赖的第三方库。

4. 自定义类加载器

  • 实现:用于实现特殊加载逻辑(如从网络、数据库加载类)。

2、双亲委派模型

        当类加载器收到一个类加载请求时,会首先委派给我自己的父类加载器,而不是自己加载,当向上委派到启动类加载器时,若其无法加载,则该类由子类自行尝试加载。

【Java】JVM虚拟机(基本概念、类加载机制)