JVM(类的加载与ClassLoader、双亲委派机制)

这篇具有很好参考价值的文章主要介绍了JVM(类的加载与ClassLoader、双亲委派机制)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 类的生命周期

类在内存中完整的生命周期:加载-->使用-->卸载。其中加载过程又分为:装载、链接、初始化三个阶段。

JVM(类的加载与ClassLoader、双亲委派机制)

2. 类的加载过程

当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载、链接、初始化三个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载。

JVM(类的加载与ClassLoader、双亲委派机制)

类的加载又分为三个阶段:

(1)装载Loading

将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成

(2)链接Linking

验证Verify:确保加载的类信息符合JVM规范,例如:以cafebabe开头,没有安全方面的问题。

准备Prepare:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。

解析Resolve:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

(3)初始化Initialization

  • 执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。

  • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。

  • 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。

3. 类加载器(classloader)

JVM(类的加载与ClassLoader、双亲委派机制)

3.1 类加载器的作用

class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

JVM(类的加载与ClassLoader、双亲委派机制)

3.2 类加载器的分类(JDK8)

JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)自定义类加载器(User-Defined ClassLoader)

从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器。无论类加载器的类型如何划分,在程序中我们最常见的类加载器结构主要是如下情况:

JVM(类的加载与ClassLoader、双亲委派机制)

(1)启动类加载器(引导类加载器,Bootstrap ClassLoader)

  • 这个类加载使用C/C++语言实现的,嵌套在JVM内部。获取它的对象时往往返回null
  • 它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jarsun.boot.class.path路径下的内容)。用于提供JVM自身需要的类。
  • 并不继承自java.lang.ClassLoader,没有父加载器。
  • 出于安全考虑,Bootstrap启动类加载器只加载包名为javajavaxsun等开头的类
  • 加载扩展类和应用程序类加载器,并指定为他们的父类加载器。

(2)扩展类加载器(Extension ClassLoader)

  • Java语言编写,由sun.misc.Launcher$ExtClassLoader实现。
  • 继承于ClassLoader类
  • 父类加载器为启动类加载器
  • java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库。如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载。

JVM(类的加载与ClassLoader、双亲委派机制)

(3)应用程序类加载器(系统类加载器,AppClassLoader)

  • java语言编写,由sun.misc.Launcher$AppClassLoader实现
  • 继承于ClassLoader
  • 父类加载器为扩展类加载器
  • 它负责加载环境变量classpath或系统属性 java.class.path 指定路径下的类库
  • 应用程序中的类加载器默认是系统类加载器。
  • 它是用户自定义类加载器的默认父加载器
  • 通过ClassLoadergetSystemClassLoader()方法可以获取到该类加载器

(4)用户自定义类加载器(了解)

  • 在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的。在必要时,我们还可以自定义类加载器,来定制类的加载方式。
  • 体现Java语言强大生命力和巨大魅力的关键因素之一便是,Java开发者可以自定义类加载器来实现类库的动态加载,加载源可以是本地的JAR包,也可以是网络上的远程资源。
  • 同时,自定义加载器能够实现应用隔离,例如 Tomcat,Spring等中间件和组件框架都在内部实现了自定义的加载器,并通过自定义加载器隔离不同的组件模块。这种机制比C/C++程序要好太多,想不修改C/C++程序就能为其新增功能,几乎是不可能的,仅仅一个兼容性便能阻挡住所有美好的设想。
  • 自定义类加载器通常需要继承于ClassLoader。

3.3 双亲委派机制

java中类加载的过程采用双亲委派机制,加载一个类先由应用类加载器委托给扩展类加载器,再由扩展类加载器委托给启动类加载器,如果启动类加载器发现自己也加载不了的话,则由扩展类加载器加载,如果扩展类加载器也加载不了的话,则由应用类加载器加载,如果连应用类加载器都找不到的话,则报ClassNotFound的异常。

理解:

  • 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成。
  • 每一个层次的类加载器都是如此。因此,所有的加载请求最终都应该传送到顶层的启动类加载器中。
  • 只有当父加载器反馈自己无法完成这个加载请求时(搜索范围中没有找到所需的类),子加载器才会尝试自己去加载。

JVM(类的加载与ClassLoader、双亲委派机制)

同理(为什么要从下到上,再又从上到下):一个类在收到类加载请求后,如果这个类没有被加载,当前类加载器不会自己加载这个类,而是把这个类加载请求向上委派给它的父类去完成,父类收到这个请求后又继续向上委派给自己的父类加载器,以此类推,直到所有的请求委派到启动类加载器中。如果这个类不属于启动类加载器加载,又会向下委派子类加载器来加载这个类,直到这个请求被成功加载,但是一直到自定义加载器都没有找到,JVM就会抛出ClassNotFund异常。

JVM(类的加载与ClassLoader、双亲委派机制)

图片取自网络。

3.3.1 双亲委派机制优势
  • 避免类的重复加载
  • 当自己程序中定义了一个和Java.lang包同名的类,此时,由于使用的是双亲委派机制,会由启动类加载器去加载JAVA_HOME/lib中的类,而不是加载用户自定义的类。此时,程序可以正常编译,但是自己定义的类无法被加载运行。
  • 保护程序安全,防止核心API被随意篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。
    • 例如:自己定义了一个java.lang.String的类,首先会由应用类加载器加载,应用类加载器识别到这个类不属于我加载,因为是JVM系统级别的类,此时应用程序加载器不会加载,会向上委托,直到启动类加载器,启动类加载器识别到这个类属于我来加载,就会去JAVA_HOME下去加载相同包名的String类。加载完成后,即使再有相同包名类名的String类也不会去加载,因为相同包名的类已经被加载过了,就会造成即使开发人员定义了和JVM级别相同包的类也不会去加载自己定义的类,保证了系统级别的类加载安全性,防止核心API被随意篡改。

3.4 查看某个类的类加载器对象

(1)获取默认的系统类加载器

ClassLoader classloader = ClassLoader.getSystemClassLoader();

(2)查看某个类是哪个类加载器加载的

ClassLoader classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader();

//如果是根加载器加载的类,则会得到null
ClassLoader classloader1 = Class.forName("java.lang.Object").getClassLoader();

(3)获取某个类加载器的父加载器

ClassLoader parentClassloader = classloader.getParent();

示例代码:


import org.junit.Test;

public class TestClassLoader {
    @Test
    public void test01(){
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println("systemClassLoader = " + systemClassLoader);
    }

    @Test
    public void test02()throws Exception{
        ClassLoader c1 = String.class.getClassLoader();
        System.out.println("加载String类的类加载器:" + c1);

        ClassLoader c2 = Class.forName("sun.util.resources.cldr.zh.TimeZoneNames_zh").getClassLoader();
        System.out.println("加载sun.util.resources.cldr.zh.TimeZoneNames_zh类的类加载器:" + c2);

        ClassLoader c3 = TestClassLoader.class.getClassLoader();
        System.out.println("加载当前类的类加载器:" + c3);
    }

    @Test
    public void test03(){
        ClassLoader c1 = TestClassLoader.class.getClassLoader();
        System.out.println("加载当前类的类加载器c1=" + c1);

        ClassLoader c2 = c1.getParent();
        System.out.println("c1.parent = " + c2);

        ClassLoader c3 = c2.getParent();
        System.out.println("c2.parent = " + c3);

    }
}

3.5 使用ClassLoader获取流

关于类加载器的一个主要方法:getResourceAsStream(String str):获取类路径下的指定文件的输入流


InputStream in = null;
in = this.getClass().getClassLoader().getResourceAsStream("exer2\\test.properties");
System.out.println(in);

举例:文章来源地址https://www.toymoban.com/news/detail-435585.html

//需要掌握如下的代码
@Test
public void test5() throws IOException {
    Properties pros = new Properties();
    //方式1:此时默认的相对路径是当前的module
//        FileInputStream is = new FileInputStream("info.properties");
//        FileInputStream is = new FileInputStream("src//info1.properties");

    //方式2:使用类的加载器
    //此时默认的相对路径是当前module的src目录
    InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("info1.properties");


    pros.load(is);

    //获取配置文件中的信息
    String name = pros.getProperty("name");
    String password = pros.getProperty("password");
    System.out.println("name = " + name + ", password = " + password);
}

到了这里,关于JVM(类的加载与ClassLoader、双亲委派机制)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【JVM从入门到实战】(六)类加载器的双亲委派机制

    在Java中如何使用代码的方式去主动加载一个类呢? 方式1:使用Class.forName方法,使用当前类的类加载器去加载指定的类。 方式2:获取到类加载器,通过类加载器的loadClass方法指定某个类加载器加载。 双亲委派机制: 自底向上查找是否加载过,再由顶向下进行加载 面试题:

    2024年02月04日
    浏览(33)
  • [JVM] 3. 类加载子系统(2)-- 类加载器、双亲委派机制(JDK1.8及之前)及其他

    JDK1.8及之前和JDK9及之后的双亲委派模型是不一样的,这里学习了1.8及以前的双亲委派模型,记录笔记 1.8之前主要是这几种类加载器: 负责将存放在 JAVA_HOMElib 目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名,如rt.jar)类库加载到虚

    2024年02月16日
    浏览(34)
  • 【JVM】打破双亲委派机制

           📝个人主页:五敷有你        🔥 系列专栏:JVM ⛺️ 稳中求进,晒太阳 ClassLoader包含了四个核心方法 双亲委派机制核心代码阅读 阅读双亲委派机制的核心代码,分析如何通过自定义类加载器打破双亲委派机制。 打破双亲委派机制的核心就是讲下面的代码重写

    2024年02月19日
    浏览(33)
  • 【jvm】双亲委派机制

    一、说明 1.java虚拟机对class文件采用的是按需加载的方式,当需要使用该类时才会将它的class文件加载到内存生成class对象 2.加载某个类的class文件时,java虚拟机采用双亲委派模式,即把请求交给由父类处理,是一种任务委派模式 3.jvm中表示两个class对象是否为同一个类存在两

    2024年02月11日
    浏览(29)
  • JVM类加载&双亲委派-JVM(二)

    上篇文章说了java类的加载,验证、准备、解析、初始化。类的初始化必须是类加载完才执行,所以类的构造方法初始化是在静态方法之后执行。 JVM类加载机制-JVM(一) 一、类加载和双亲委派机制 前面类加载主要通过类加载器实现,类加载器有这几种: 引导类加载器:负责

    2024年02月11日
    浏览(29)
  • JVM——类的生命周期(加载阶段,连接阶段,初始化阶段)

    类的生命周期 ⚫ 1、加载(Loading)阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息。 程序员可以使用Java代码拓展的不同的渠道。 ⚫ 2、类加载器在加载完类之后,Java虚拟机会将字节码中的信息保存到方法区中。 ⚫ 3、类加载器在加载

    2024年02月05日
    浏览(32)
  • 类加载 - 双亲委派机制详解

    启动类加载器(Bootstrap Class Loader):它是Java虚拟机的一部分,负责加载Java核心类库,如java.lang包中的类。它是最顶层的类加载器,由C++实现,不是Java类。 扩展类加载器(Extension Class Loader):它负责加载Java的扩展类库,位于JRE的lib/ext目录下。它是由Java编写的类加载器,是

    2024年02月12日
    浏览(30)
  • JVM类加载基本流程及双亲委派模型

    一个运行起来的Java进程就是一个JVM虚拟机,这就需要从操作系统中申请一片内存区域。JVM申请到内存之后,会把这个内存划分为几个区域,每个区域都有各自的作用。 一般会把内存划分为 四个区域 : 方法区 (也称 \\\"元数据区\\\"), 堆区 , 栈区 , 程序计数器 。 (1)方法区:

    2024年04月23日
    浏览(28)
  • Java JVM类加载阶段 双亲委派模式

    加载 将类的字节码载入方法区中,内部采用 C++ 的 instanceKlass 描述 java 类,它的重要 field 有: _java_mirror 即 java 的类镜像,例如对 String 来说,就是 String.class,作用是把 klass 暴露给 java 使用 _super 即父类 _fields 即成员变量 _methods 即方法 _constants 即常量池 _class_loader 即类加载器

    2024年01月25日
    浏览(30)
  • JVM的类加载的过程以及双亲委派模型

    目录 1、加载 (加载字节码文件,生成.class对象) 2、验证(验证Class文件是否符合规范)  3、准备 (为静态变量分配内存并设置变量初始值) 4、解析(初始化常量池中的一些常量)  5、初始化(初始化对象,并为静态变量赋值)  总结: 双亲委派模型:   JVM的类加载器

    2023年04月20日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包