JVM 类加载器机制(上)

简介: 本文主要是讲述 JVM 类加载过程和 JVM 提供的集中类加载器以及双亲委派机制,通过 Tomcat 的类加载机制阐述如何打破双亲委派机制的方法。

类加载过程


类加载的时机


一个类型被加载到虚拟机内存中开始,到卸载出内存为止、它的整个生命周期将会经历加载、验证、准备、解析、初始化、使用、卸载七个阶段。其中验证、准备、解析为连接。


image.png



类被主动加载的 7 种情况


  1. 创建类的实例


  1. 访问某个类或接口的静态变量,或者对该静态变量赋值


  1. 调用类的静态方法


  1. 反射(如 Class.forName("com.test.Test")


  1. 初始化一个类的子类


  1. Java虚拟机启动时被标记为启动类的类, 就是包含 main 方法的类(Java Test)


  1. JDK1.7开始提供的动态语言支持,java.lang.invoke.MethodHandle实例的解析结果REF_getStatic, REF_putStatic, REF_invokeStatic句柄对应的类没有被初始化则初始化。


其它加载情况


  1. 当 Java 虚拟机初始化一个类时,要求它所有的父类都被初始化,单这一条规则并不适用于接口。


  • 在初始化一个类时,并不会先初始化它所实现的接口
  • 在初始化一个接口时,并不会先初始化它的父类接口
  • 因此,一个父接口并不会因为他的子接口或者实现了类的初始化而初始化,只有当程序首次被使用特定接口的静态变量时,才会导致该接口的初始化。


  1. 只有当前程序访问的静态变量或静态方法确实在当前类或当前接口定义时,才可认为是对接口或类的主动使用。


  1. 调用 ClassLoader 类的 loadClass 方法加载一类,并不是对类的主动使用,不会导致类的初始化。


测试例子 1:


public class Test_2 extends Test_2_A {
    static {
        System.out.println("子类静态代码块");
    }
    {
        System.out.println("子类代码块");
    }
    public Test_2() {
        System.out.println("子类构造方法");
    }
    public static void main(String[] args) {
        new Test_2();
    }
}
class Test_2_A {
    static {
        System.out.println("父类静态代码块");
    }
    {
        System.out.println("父类代码块");
    }
    public Test_2_A() {
        System.out.println("父类构造方法");
    }
    public static void find() {
        System.out.println("静态方法");
    }
}
//代码块和构造方法执行顺序
//1).父类静态代码块
//2).子类静态代码块
//3).父类代码块
//4).父类构造方法
//5).子类代码块
//6).子类构造方法


测试例子 2:


public class Test_1 {
    public static void main(String[] args) {
        System.out.println(Test_1_B.str);
    }
}
class Test_1_A {
    public static String str = "A str";
    static {
        System.out.println("A Static Block");
    }
}
class Test_1_B extends Test_1_A {
    static {
        System.out.println("B Static Block");
    }
}
//输出结果
//A Static Block
//A str


加载


在硬盘上查找并且通过 IO 读入字节码文件,使用到该类的时候才会被加载,例如调用 main 方法, new 关键字调用对象等,在加载阶段会在内存中生成这个类的 java.lang.Class 对象, 作为方法区这个类的各种数据的访问入口。


验证


校验字节码文件的正确性


准备


给类的静态变量分配内存,并且赋予默认值


解析


符号引用替换为直接引用,该节点会把一些静态方法(符号引用,比如 main() 方法)替换为指向数据所存内存的指针或句柄等(直接引用),这就是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用。


初始化


对类的静态变量初始化为指定的值,执行静态代码块。


类加载器


  • 引导类加载器(Bootstrap Class Loader) 负责加载 <JAVA_HOME>\lib\ 目录或者被 -Dbootclaspath 参数指定的类,  比如: rt.jar, tool.jar 等 。


  • 拓展类加载器(Extension Class Loader)  负责加载 <JAVA_HOME>\lib\ext\-Djava.ext.dirs 选项所指定目录下的类和 jar包。


  • 应用程序类加载器(System Class Loader) 负责加载 CLASSPATH-Djava.class.path所指定的目录下的类和 jar 包。


  • 自定义类加载器:负责加载用户自定义包路径下的类包,通过 ClassLoader 的子类实现 Class 的加载。


测试文件:


public class TestJVMClassLoader {
    public static void main(String[] args) {
        System.out.println(String.class.getClassLoader());
        System.out.println(DESKeyFactory.class.getClassLoader());
        System.out.println(TestJVMClassLoader.class.getClassLoader());
        System.out.println();
        ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
        ClassLoader extClassLoader = appClassLoader.getParent();
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        System.out.println("bootstrapClassLoader: " + bootstrapClassLoader);
        System.out.println("extClassLoader: " + extClassLoader);
        System.out.println("appClassLoader: " + appClassLoader);
        System.out.println();
        System.out.println("bootstrapLoader 加载以下文件:");
        URL[] urls = Launcher.getBootstrapClassPath().getURLs();
        for (URL url : urls) {
            System.out.println(url);
        }
        System.out.println();
        System.out.println("extClassLoader 加载以下文件:");
        System.out.println(System.getProperty("java.ext.dirs"));
        System.out.println();
        System.out.println("appClassLoader 加载以下文件:");
        System.out.println(System.getProperty("java.class.path"));
    }
}


相关文章
|
4月前
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
399 55
|
4月前
|
Arthas Java 测试技术
JVM深入原理(六)(一):JVM类加载器
目录6. JVM类加载器6.1. 类加载器-概述6.2. 类加载器-执行流程6.3. 类加载器-分类(JDK8)6.3.1. JVM底层实现的类加载器6.3.1.1. 启动类加载器6.3.2. Java代码实现类的加载器6.3.2.1. 扩展类加载器6.3.2.2. 应用程序类加载器6.4. 类加载器-Arthas查看类加载器
58 0
|
4月前
|
Java 关系型数据库 MySQL
JVM深入原理(六)(二):双亲委派机制
自定义类加载器打破双亲委派机制的方法:复写ClassLoader中的loadClass方法常见问题:要加载的类名如果是以java.开头,则会抛出安全性异常加载自定义的类都会有一个共同的父类Object,需要在代码中交由父类加载器去加载自定义类加载器不手动指定parent会默认指定应用类加载两个自定义类加载器加载同一个类会被认为是两个对象,只有相同的类加载器+想通的类限定名才会被认为是一个对象。
139 0
|
安全 前端开发 Java
【JVM的秘密揭秘】深入理解类加载器与双亲委派机制的奥秘!
【8月更文挑战第25天】在Java技术栈中,深入理解JVM类加载机制及其双亲委派模型是至关重要的。JVM类加载器作为运行时系统的关键组件,负责将字节码文件加载至内存并转换为可执行的数据结构。其采用层级结构,包括引导、扩展、应用及用户自定义类加载器,通过双亲委派机制协同工作,确保Java核心库的安全性与稳定性。本文通过解析类加载器的分类、双亲委派机制原理及示例代码,帮助读者全面掌握这一核心概念,为开发更安全高效的Java应用程序奠定基础。
138 0
|
12月前
|
安全 Java 应用服务中间件
JVM常见面试题(三):类加载器,双亲委派模型,类装载的执行过程
什么是类加载器,类加载器有哪些;什么是双亲委派模型,JVM为什么采用双亲委派机制,打破双亲委派机制;类装载的执行过程
273 35
JVM常见面试题(三):类加载器,双亲委派模型,类装载的执行过程
|
11月前
|
缓存 前端开发 Java
JVM知识体系学习二:ClassLoader 类加载器、类加载器层次、类过载过程之双亲委派机制、类加载范围、自定义类加载器、编译器、懒加载模式、打破双亲委派机制
这篇文章详细介绍了JVM中ClassLoader的工作原理,包括类加载器的层次结构、双亲委派机制、类加载过程、自定义类加载器的实现,以及如何打破双亲委派机制来实现热部署等功能。
472 3
|
11月前
|
前端开发 Java 应用服务中间件
JVM进阶调优系列(1)类加载器原理一文讲透
本文详细介绍了JVM类加载机制。首先解释了类加载器的概念及其工作原理,接着阐述了四种类型的类加载器:启动类加载器、扩展类加载器、应用类加载器及用户自定义类加载器。文中重点讲解了双亲委派机制,包括其优点和缺点,并探讨了打破这一机制的方法。最后,通过Tomcat的实际应用示例,展示了如何通过自定义类加载器打破双亲委派机制,实现应用间的隔离。
|
12月前
|
Arthas Java 测试技术
JVM —— 类加载器的分类,双亲委派机制
类加载器的分类,双亲委派机制:启动类加载器、扩展类加载器、应用程序类加载器、自定义类加载器;JDK8及之前的版本,JDK9之后的版本;什么是双亲委派模型,双亲委派模型的作用,如何打破双亲委派机制
JVM —— 类加载器的分类,双亲委派机制
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
293 0
|
开发者 C# Windows
WPF布局大揭秘:掌握布局技巧,轻松创建响应式用户界面,让你的应用程序更上一层楼!
【8月更文挑战第31天】在现代软件开发中,响应式用户界面至关重要。WPF(Windows Presentation Foundation)作为.NET框架的一部分,提供了丰富的布局控件和机制,便于创建可自动调整的UI。本文介绍WPF布局的基础概念与实现方法,包括`StackPanel`、`DockPanel`、`Grid`等控件的使用,并通过示例代码展示如何构建响应式布局。了解这些技巧有助于开发者优化用户体验,适应不同设备和屏幕尺寸。
442 0