精华内容
下载资源
问答
  • JVM类加载机制详解(一)JVM类加载过程
    2021-04-14 11:11:44

    首先Throws(抛出)几个自己学习过程中一直疑惑的问题:

    1、什么是类加载?什么时候进行类加载?

    2、什么是类初始化?什么时候进行类初始化?

    3、什么时候会为变量分配内存?

    4、什么时候会为变量赋默认初值?什么时候会为变量赋程序设定的初值?

    5、类加载器是什么?

    6、如何编写一个自定义的类加载器?


    首先,在代码编译后,就会生成JVM(Java虚拟机)能够识别的二进制字节流文件(*.class)。而JVM把Class文件中的类描述数据从文件加载到内存,并对数据进行校验、转换解析、初始化,使这些数据最终成为可以被JVM直接使用的Java类型,这个说来简单但实际复杂的过程叫做JVM的类加载机制。


    Class文件中的“类”从加载到JVM内存中,到卸载出内存过程有七个生命周期阶段。类加载机制包括了前五个阶段。

    如下图所示:

    其中,加载、验证、准备、初始化、卸载的开始顺序是确定的,注意,只是按顺序开始,进行与结束的顺序并不一定。解析阶段可能在初始化之后开始。


    另外,类加载无需等到程序中“首次使用”的时候才开始,JVM预先加载某些类也是被允许的。(类加载的时机)

    一、类的加载

    我们平常说的加载大多不是指的类加载机制,只是类加载机制中的第一步加载。在这个阶段,JVM主要完成三件事:


    1、通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件)。而获取的方式,可以通过jar包、war包、网络中获取、JSP文件生成等方式。

    2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。这里只是转化了数据结构,并未合并数据。(方法区就是用来存放已被加载的类信息,常量,静态变量,编译后的代码的运行时内存区域)

    3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。这个Class对象并没有规定是在Java堆内存中,它比较特殊,虽为对象,但存放在方法区中。


    二、类的连接

    类的加载过程后生成了类的java.lang.Class对象,接着会进入连接阶段,连接阶段负责将类的二进制数据合并入JRE(Java运行时环境)中。类的连接大致分三个阶段。

    1、验证:验证被加载后的类是否有正确的结构,类数据是否会符合虚拟机的要求,确保不会危害虚拟机安全。

    2、准备:为类的静态变量(static filed)在方法区分配内存,并赋默认初值(0值或null值)。如static int a = 100;

    静态变量a就会在准备阶段被赋默认值0。

    对于一般的成员变量是在类实例化时候,随对象一起分配在堆内存中。

    另外,静态常量(static final filed)会在准备阶段赋程序设定的初值,如static final int a = 666;  静态常量a就会在准备阶段被直接赋值为666,对于静态变量,这个操作是在初始化阶段进行的。

    3、解析:将类的二进制数据中的符号引用换为直接引用。

    三、类的初始化

    类初始化是类加载的最后一步,除了加载阶段,用户可以通过自定义的类加载器参与,其他阶段都完全由虚拟机主导和控制。到了初始化阶段才真正执行Java代码。

    类的初始化的主要工作是为静态变量赋程序设定的初值。

    如static int a = 100;在准备阶段,a被赋默认值0,在初始化阶段就会被赋值为100。


    Java虚拟机规范中严格规定了有且只有五种情况必须对类进行初始化:

    1、使用new字节码指令创建类的实例,或者使用getstatic、putstatic读取或设置一个静态字段的值(放入常量池中的常量除外),或者调用一个静态方法的时候,对应类必须进行过初始化。

    2、通过java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则要首先进行初始化。

    3、当初始化一个类的时候,如果发现其父类没有进行过初始化,则首先触发父类初始化。

    4、当虚拟机启动时,用户需要指定一个主类(包含main()方法的类),虚拟机会首先初始化这个类。

    5、使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、RE_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行初始化,则需要先触发其初始化。

    注意,虚拟机规范使用了“有且只有”这个词描述,这五种情况被称为“主动引用”,除了这五种情况,所有其他的类引用方式都不会触发类初始化,被称为“被动引用”。

    被动引用的例子一:

    通过子类引用父类的静态字段,对于父类属于“主动引用”的第一种情况,对于子类,没有符合“主动引用”的情况,故子类不会进行初始化。代码如下:


    //父类
    public class SuperClass {
        //静态变量value
        public static int value = 666;
        //静态块,父类初始化时会调用
        static{
            System.out.println("父类初始化!");
        }
    }
     
    //子类
    public class SubClass extends SuperClass{
        //静态块,子类初始化时会调用
        static{
            System.out.println("子类初始化!");
        }
    }
     
    //主类、测试类
    public class NotInit {
        public static void main(String[] args){
            System.out.println(SubClass.value);
        }
    }
    输出结果:


    被动引用的例子之二:

    通过数组来引用类,不会触发类的初始化,因为是数组new,而类没有被new,所以没有触发任何“主动引用”条款,属于“被动引用”。代码如下:


    //父类
    public class SuperClass {
        //静态变量value
        public static int value = 666;
        //静态块,父类初始化时会调用
        static{
            System.out.println("父类初始化!");
        }
    }
     
    //主类、测试类
    public class NotInit {
        public static void main(String[] args){
            SuperClass[] test = new SuperClass[10];
        }
    }
    没有任何结果输出!

    被动引用的例子之三:

    刚刚讲解时也提到,静态常量在编译阶段就会被存入调用类的常量池中,不会引用到定义常量的类,这是一个特例,需要特别记忆,不会触发类的初始化!


    //常量类
    public class ConstClass {
        static{
            System.out.println("常量类初始化!");
        }
        
        public static final String HELLOWORLD = "hello world!";
    }
     
    //主类、测试类
    public class NotInit {
        public static void main(String[] args){
            System.out.println(ConstClass.HELLOWORLD);
        }
    }

     

    更多相关内容
  • JVM类加载器详解

    千次阅读 2021-01-11 23:12:03
    前言 ...类加载器是什么?有什么作用? 1、负责从文件或者网络加载class文件字节流 2、读取字节码中的信息,并在运行时存储到JVM的内存区域 3、检查并确保加载的class文件信息符合JVM字节码规范 ...

    前言

    在上一篇中,通过下面这幅图大致了JVM整体的内部运行结构图,在JVM的结构中,类加载子系统作为连接外部class文件与真正将class文件加载到运行时数据区,承担着重要的作用
    在这里插入图片描述

    类加载器是什么?有什么作用?

    • 读取字节码文件,以二进制流的形式读取
    • 解析字节码二进制流的静态数据,转换为运行时JVM方法区的数据
    • 生成类的java.lang.class对象,放入堆中,作为方法区的访问入口
    • 在加载某个类的过程中,如果存在继承父类的情况,会同时触发父类加载

    通俗来讲就是,类加载器确保加载到JVM内存中的字节码信息符合JVM运行时的规范,以便正确的执行class中的信息

    下面是一副类加载器加载字节码的简易流程图过程,下面将结合这张图,通俗的对类加载器的执行过程做简单的说明

    在这里插入图片描述

    1、加载阶段(loading)

    以某个具体的Class文件加载为例,当程序需要XXX.Class文件时,类加载器将该类的字节码文件读取之后加载到运行时数据区,具体加载到哪里去呢?很明显,首先会去堆中查找一下当前class的实例是否已存在?如果存在了,就直接从堆区取出来加载到方法区即可,否则执行上一个图的加载过程,在反射的使用中,经常看到 XXX.getClass()获取Class的信息就是这个道理
    在这里插入图片描述

    说到这,顺便总结下,Class的实例什么时候创建呢?主要包括下面几种:

    1. 使用new 关键字
    2. 反射Class.forName(“类的全限定名”)
    3. 子类加载时同时加载父类
    4. JVM启动时,包含main方法的主类

    2、链接(linking)

    该阶段主要做的事情包括:

    • 验证:确保字节码符合虚拟机的规范要求
    • 准备:为字段赋予初始值
    • 解析:符号引用转为直接引用

    验证:
    在这里插入图片描述
    准备:

    为类中相关的字段赋予初始值,比如像下面这样的,为类的静态变量赋予初始值

    public class A {
    private static int XXX =10;
    }

    解析:

    将字节码中的静态字面关联转换为JVM内存中动态指针关联,举例来说,当A类extendsB类的时候,从语义上分析来说,表现出下面这样
    在这里插入图片描述

    即类加载器加载class时候读取到的A类和B类之间的关系可以理解为使用字面量的表达,但是JVM并不认识啊,在JVM中,需要使用一种新的形式去描述它们之间的关系,即动态指针的关联

    而具体解析字节码中的信息包括下面几种:

    • 类解析
    • 字段解析
    • 方法解析
    • 接口解析

    在这里插入图片描述

    3、初始化Initialization

    初始化阶段要做的事情主要是:执行类的构造方法()的过程

    clint完成类的初始化工作,该方法不需要显式声明,由Java编译器自动生成,这里和上面的,加载/验证/准备/解析有所不同的是,上面几个阶段是由虚拟机主导的,与代码无关,

    而初始化则是通过代码生成clint来完成类的初始化过程

    初始化过程总结:

    • 初始化阶段对类(静态变量)赋值与执行static代码块
    • 子类初始化过程会优先执行父类初始化clint
    • 没有类变量及static代码块就不会产生初始化的clint
    • 使用TraceClassLoading可以查看类的加载过程
    • clint方法会默认增加同步锁,确保clint的初始化只进行一次

    类加载器

    上面简单聊了一下JVM类加载器加载时的几个重要的执行步骤,既然说到类加载器,不得不谈谈在JVM中,类加载具体有哪些呢?

    按照层次划分,类加载器大概可以分为下面几种:

    • 启动类加载器 BootstrapClassLoader
    • 扩展类加载器 ExtentionClassLoader
    • 应用程序类加载器 AppClassLoader

    它们之间的关系可以用下图表示:
    在这里插入图片描述

    其中,启动类加载器,扩展类加载器,应用程序类加载器属于JVM自身的加载器,而自定义加载器在使用中更加灵活,不仅可以加载自定义目录中的class,还能加载来自网络输入的二进制流,需要说明的是,它们之间并非是自上而下的继承关系,仅为上下级

    启动类加载器:

    1. 使用C语言开发
    2. 用于加载Java核心类库
      比如 : JAVA_HOME/jre/lib/rt.jar …
      ${sun.boot.class.path}路径下的jar
    3. 基于沙箱机制,只加载java,javax,sun包开头的类

    扩展类加载器:

    1. Java语言编写
    2. 上级加载器为启动类加载器
    3. 加载${JAVA_HOME}/jre/lib/ext扩展目录下的类库,如果你开发的Jar,放入该目录,也会被扩展类加载器加载

    应用程序类加载器 (AppClassLoader )

    • Java语言编写,由sun.misc.Launcher$AppClassLoader 实现
    • 应用程序类加载器是默认的加载器,绝大多数类都是由它加载
    • 上级加载器为扩展类加载器
    • 它负责加载classpath下的应用程序(三方)类库

    下面来简单看一段具体的实例代码

    public class TestClassLoader {
    
        public static void main(String[] args) {
    
            //自定义的类,默认使用应用类加载器
            ClassLoader classLoader = TestClassLoader.class.getClassLoader();
            System.out.println(classLoader);
    
            //扩展类加载器
            ClassLoader extClassLoader = SunEC.class.getClassLoader();
            System.out.println(extClassLoader);
    
            //启动类加载器,由于启动类加载器是由C语言编写的,不能被JVM管理,所以返回null
            ClassLoader bootstrapClassLoader = Object.class.getClassLoader();
            System.out.println(bootstrapClassLoader);
        }
    
    }
    

    在这里插入图片描述
    通过定位,可以依次找到各个加载器的位置
    在这里插入图片描述
    在这里插入图片描述

    类加载器的双亲委派模型

    上面我们了解了JVM中常用的几种类加载器,细心的小伙伴应该留意到,3者之间存在着一种上下级关系,这种关系的结构通俗解释就是“双亲委派”

    何为“双亲委派”?

    • 加载类时,加载器逐级将需要加载的任务向上委派至引导类加载器,然后再逐级向下尝试加载,直至加载完成
    • 优点:该机制保护了类不会被重复加载,同时该机制提供了沙箱机制,禁止用户污染java开头的核心包

    比如说,当JVM需要运行一个自定义的类时,自定义加载器由于存在上级扩展类加载器和引导类加载器,因此会逐级向上加载,但是上面的2级加载器真正开始加载时,发现自定义的类中的相关信息不在自己这一层加载器所能服务的范围,因此又自顶向下逐级加载,最终来到能够处理的加载器中进行处理

    从下面这个简单的案例,可以发现,当我们自定义的类和系统的加载器中的核心包里面的类发生冲突时就报出下面的错误了,这也是双亲委派的沙箱机制的好处
    在这里插入图片描述

    本篇主要讲述了类加载器相关的内容,希望对看到的同学有用,本篇到此结束最后感谢观看!

    展开全文
  • jvm 类加载器详解

    2018-09-18 22:46:04
    类加载器 想说明白什么是类加载器需要先明白几个关键问题: 第一个问题: 如何确定Java虚拟机中一个的唯一性?虚拟机如何避免一个被重复加载? 虚拟机中有多个类加载器,只有这个类加载器关联才可以...

    类加载器

    想说明白什么是类加载器需要先明白几个关键问题:

    第一个问题:
    如何确定Java虚拟机中一个类的唯一性?虚拟机如何避免一个类被重复加载?
    虚拟机中有多个类加载器,只有这个类和类加载器关联才可以确定其唯一性。也就是说一个类如果被不同类加载器加载了,就会生成多个逻辑上完全没有关系的类。
    
    第二个问题:
    什么是类加载器?他的作用是什么?
    负责动态将Java类加载到Java虚拟机内存空间中。
    我认为,是它将硬盘中编译好的.class文件加载导入到内存中变为一个class对象(堆中)和相关class信息(元空间)。我们创建对象全靠这些信息。
    
    第三个问题:	
    为什么要有很多不一样的类加载器?
    区分同名的类,这里的同名类,来自不同的jar包,他们不止类名相同,包名也都一样。可通过自己定义自己的类加载器加载,防止冲突的发生。其实主要还是防止jdk自带的类和用户自己定义的类冲突。
    
    第四个问题:
    类加载器的加载机制是什么?为什么要使用这个机制?如何避免同一个类被重复加载?
    使用双亲委派机制,类加载器通过双亲委派机制防止一个类被多个类加载器加载。
    
    1. 加粗样式双亲委派机制:

      类加载器会先将类传递给更高级别的类加载器加载;若没有更高的类加载器,或者更高级别的类加载器无法加载,才会自己加载。

      这种方法成功的避免了同一个类被不同的类加载器重复加载。

    2. jvm自带类加载器

      • 启动/引导类加载器(bootstrap classLoader)
        最高级别的类加载器,使用C++实现,加载javahome\lib中的jar包时使用,只能加载特定的jar包,如:rt.jar。引用方式很特别,指定双亲为null时自动引用。
      • 扩展类加载器(extension classLoader)
        加载javahome\lib\ext中的文件,可被开发者直接使用。
      • 应用程序/系统类加载器(Extension classloader)
        加载用户路径(classpath)下的所有类。也是默认的类加载器

    这三种类加载器的传递方式为:

    类加载过程

    类加载有一个先对固定的顺序:

    加载、验证、准备、初始化和卸载这5个阶段顺序是确定的,类的加载过程必须按照这种顺序开始, 而解析阶段则不一定: 它在某些情况下可以在初始化阶段之后再开始, 这是了支持Java语言的运行时绑定。

    加载

    三个操作:
    1 通过类的全选定名获取这个类的字节流。
    2 将这个字节流(字节流代表某种静态结构)转化为方法区运行时数据结构。
    3 生成一个这个类的class对象,方法去数据的入口。

    数组类情况比较特殊,它不是由一个类加载器加载的,它被虚拟机直接加载。
    数组的组件类型(String[] 的组件类型就是String)是引用类型(一个类),则先加载组件类型,之后在加载这个组件类型的类加载器上关联这个数组。用于确定数组的唯一性。
    数组的组件类型不是引用类型,数组会直接与引导类加载器关联

    验证

    确保虚拟机引入的class文件字节流是安全的。防止恶意代码攻击的发生。
    四个操作:
    1 文件格式验证
    class文件符合规范,且能这个版本的虚拟机运行。
    已特定魔数开头,版本号再虚拟机处理范围之内,常量池中的常量存在,以及常量池中各个索引类型正常,文件有没有被附加或删除信息,等等。。。
    2 元数据验证
    验证文件是否符合java规范。
    类是否有父类,父类是否被final修饰,若这个类非抽象是否实现了继承类的抽象方法,是否与父类方法矛盾
    3 字节码验证
    验证程序方法体和程序块的语法符合逻辑,不会做出危害虚拟机的事情。
    确保类型转换正常,引用赋值类型正常。
    4 符号引用验证
    在第四阶段解析阶段发生,对类和常量池的引用进行验证。将符号替换成实际对象的引用
    校验是否能找到引用的对象,对象中是否有调用的方法和字段,对象和方法是否可以为当前代码所引用(private 就不可以)。

    准备

    对一切类的静态变量(被static修饰的变量)进行赋值,若次变量被final修饰则直接赋值为其等于的值或者引用的对象,若未被final修饰则赋值为0(int、byte、short)、null(对象)、0.0f(float)、\u0000(char)、false(boolean)、0.0d(double)、0L(int)。

    解析

    将常量池中的符号引用变为直接引用的过程。在变换之前会发生符号引用验证。对同一个目标的解析结果会被缓存,防止多次对同一目标进行解析。

    • 符号引用:使用一组符号来描述引用的目标。jvm规范对描述方式未明确定义,只要求准确即可。
    • 直接引用:指向目标的指针,偏移量或者句柄。

    解析种类:
    1 类与接口解析:设被解析者为N,N的引用者为D,N代表的类或为C
    a、若C不是一个数组,虚拟通过把N全限定名传递给D的类加载器来加载C。
    b、若C是一个数组类型,数组的元素类型为对象,那么会优先加载数组的元素类型,并生成数组对象。
    c、若以上步骤正常进行,则C加载完成成为一个有效类或接口。此时进行符号引用解析,若无法引用抛出IllegalAccessError异常,若成功则进行赋值。

    2 字段解析:设这个字段的拥有者为C(类),需要的字段为B
    a、首先解析C。
    b、若C中存在与B描述一致的字段,则直接饮用这个字段。
    c、否则,若C中实现了接口,会递归查找他的父接口中有无符合B描述的字段,若存在则直接引用。
    d、否则,若C不是Object类,递归其父类,查找符合B描述的字段,若存在则直接引用,不存在或者C就是Object,直接抛出NoSuchMethodError异常。
    注意:若父类和父接口同时存在,会发生编译错误。
    注意:最后会进行字段权限的验证,若不具备方法引用条件会抛出IllegalAccessError异常

    3 类方法解析:设这个字段的拥有者为C(类),需要的字段为B
    a、首先解析C。若发现C是接口直接抛出IncompatibleClassChangeError异常。
    b、若C中有对应的方法,直接引用。
    c、否则递归查找其父类,直到结束。若存在则引用。
    d、否则查找父接口,若存在抛出AbstractMethodError 异常,否则抛出NoSuchMethodError 异常。
    注意:最后会进行方法权限的验证,若不具备方法引用条件会抛出IllegalAccessError异常

    4 接口方法解析
    a、如果发现这是个类不是接口,则抛出IncompatibleClassChangeError异常。
    b、否则在此接口中匹配方法。找到则直接引用。
    c、否则递归查找其父接口,直到object类,若有匹配的则引用。
    d、否则抛出NoSuchMethodError异常。

    初始化

    执行jvm的构造器(clinit()方法)。将类中所有变量赋值动作、静态语句块的动作合并成构造器,执行。
    jvm的类构造器会先执行父类构造器。
    jvm的接口构造器不会先执行父接口构造器,除非父类定义的变量被显式调用。
    jvm的构造器都是线程安全的。
    简而言之,类初始化时代码块和初始化方法执行顺序如下图:

    注意:这个顺序和代码块初始化方法的位置无关

    声明:部分文字图片来自 周志明. 深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)。

    展开全文
  • Class.forName("com.mysql.jdbc.Driver")
  • JVM类加载器详解

    千次阅读 多人点赞 2021-04-10 13:13:45
    JVM类加载器 1.加载子系统的作用 类加载器子系统负责从文件系统或者网络中加载class文件,class文件在文件开头有特定的文件标识。 2.加载过程 当程序主动使用某个时,如果该还未被加载到内存中,则JVM会...

    JVM类加载器

    1.类加载子系统的作用

    类加载

    类加载器子系统负责从文件系统或者网络中加载class文件,class文件在文件开头有特定的文件标识。

    2.类加载过程

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

    类被加载到 JVM 开始,到卸载出内存,整个生命周期如图:

    生命周期

    1.加载
    1. 通过类名(地址)获取此类的二进制字节流。
    2. 将这个字节流所代表的静态存储结构转换为方法区(元空间)的运行时结构。
    3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口。
    2.链接

    就是将已经读入内存的类的二进制数据合并到JVM运行时环境中去,包含以下步骤:

    1. 验证

      检验被加载的类是否有正确的内部结构,并和其他类协调一致。

    2. 准备

      准备阶段则负责为类的静态属性分配内存,并设置默认初始值;不包含final修饰的static实例变量,在编译时进行初始化。不会为实例变量初始化。

    3. 解析

      将类的二进制数据中的符号引用替换成直接引用。

    3.初始化

    类什么时候初始化?

    1. 创建类的实例,new对象
    2. 访问某个类或接口的静态变量,或者对该静态变量赋值
    3. 调用类的静态方法
    4. 反射(Class.forName(" "))
    5. 初始化一个类的子类(首先会初始化子类的父类)
    6. JVM启动时标明的启动类,即文件名和类名相同的那个类

    注意:对于一个final类型的静态变量,如果该变量的值在编译时就可以确定下来,那么这个变量相当于“宏变量”。Java编译器会在编译时直接把这个变量出现的地方替换成它的值,因此即使程序使用该静态变量,也不会导致该类的初始化。反之,如果final类型的静态Field的值不能在编译时确定下来,则必须等到运行时才可以确定该变量的值,如果通过该类来访问它的静态变量,则会导致该类被初始化。

    类的初始化顺序

    对static修饰的变量或语句块进行赋值。

    如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。

    如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。

    顺序是:父类static -> 子类static -> 父类构造方法 -> 子类构造方法

    3.类加载器分类

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

    无论类加载器的类型如何划分,在程序中我们最常见的类加载器始终只有3个:

    加载器

    1. 自定义类加载器(User-Defined ClassLoader)

      从概念上来讲,自定义类加载器一般指的是程序汇总有开发人员自定义的一类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器。

      1. 扩展类加载器(Extension ClassLoader)

        Java语言编写的,由sun.misc.Launcher$ExtClassLoader实现,父类加载器为null。

        派生于ClassLoader类。

        上层类加载器为引导类加载器。

        它负责加载JRE的扩展目录。

        从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK系统安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的jar放在此目录下,也会自动由扩展类加载器加载。

      2. 应用程序类加载器(系统类加载器 Application ClassLoader)

        Java语言编写的,由sun.misc.Launcher$AppClassLoader实现,父类加载器为ExtClassLoader。

        派生于ClassLoader类。

        上层类加载器为扩展类加载器。

        加载我们自己定义的类。

        该类加载器是程序中默认的类加载器。

        通过类名.class.getClassLoader(),ClassLoader.getSystemClassLoader()来获得。

    2. 引导类加载器(启动类加载器/根类加载器)(Bootstrap ClassLoader)

      这个类加载器使用C/C++语言实现,嵌套在JVM内部。用来加载Java核心类库。

      并不继承于java.lang.ClassLoader没有父加载器。

      负责加载扩展类加载器和应用类加载器,并为它们指定父类加载器。

      出于安全考虑,引用类加载器只加载器包名为java,javax,sun等开头的类。

    注意:ClassLoader类,它是一个抽象类,其后所有类加载器都继承自ClassLoader(不包括启动类加载器)

    类加载器加载Class大致要经过如下8个步骤:

    1. 检测此Class是否载入过,即在缓冲区中是否有此Class,如果有直接进入第8步,否则进入第2步。
    2. 如果没有父类加载器,则要么Parent是根类加载器,要么本身就是根类加载器,则跳到第4步,如果父类加载器存在,则进入第3步。
    3. 请求使用父类加载器去载入目标类,如果载入成功则跳至第8步,否则接着执行第5步。
    4. 请求使用根类加载器去载入目标类,如果载入成功则跳至第8步,否则跳至第7步。
    5. 当前类加载器尝试寻找Class文件,如果找到则执行第6步,如果找不到则执行第7步。
    6. 从文件中载入Class,成功后跳至第8步。
    7. 抛出ClassNotFountException异常。
    8. 返回对应的java.lang.Class对象。

    4.类加载机制JVM的类加载机制主要有3种

    JVM的类加载机制主要有3种

    1. 全盘负责:所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另一个类加载器来载入。
    2. 双亲委派:所谓的双亲委派,则是先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才自己去加载。
    3. 缓存机制:缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。这就是为什么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。

    细讲一下双亲委派机制(面试):

    工作原理:

    如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器区执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父加载器无法完成此加载任务,子加载器才会尝试自己去加载,如果均加载失败,就会抛出ClassNotFoundException异常,这就是双亲委派模式。即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了了时,儿子自己才想办法去完成。

    双亲委派

    双亲委派优点

    1. 安全,可避免用户自己编写的类动态替换Java的核心类,如java.lang.String。,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。
    2. 避免全限定命名的类重复加载(使用了findLoadClass()判断当前类是否已加载)。Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。5

    5.沙箱安全机制

    作用:防止恶意代码污染java源代码。

    比如我们定义了一个类名为String所在包也命名为java.lang,因为这个类本来属于jdk的,如果没有沙箱安全机制,这个类将会污染到系统中的String,但是由于沙箱安全机制,所以就委托顶层的引导类加载器查找这个类,如果没有的话就委托给扩展类加载器,再没有就委托到系统类加载器。但是由于String就是jdk源代码,所以在引导类加载器那里就加载到了,先找到先使用,所以就使用引导类加载器里面的String,后面的一概不能使用,这就保证了不被恶意代码污染。

    6.类的主动使用/被动使用

    JVM规定,每个类或者接口被首次主动使用时才对其进行初始化,有主动使用,自然就有被动使用。

    主动使用

    1. 通过new关键字被导致类的初始化,导致类的加载并初始化。
    2. 访问类或接口的静态变量,包括读取和更新,或者对该静态变量赋值。
    3. 访问类的静态方法。
    4. 对某个类进行反射操作,会导致类的初始化。
    5. 初始化子类会导致父类的初始化。
    6. 执行该类的main函数。
    7. Java虚拟机启动时被表明为启动类的类(JavaTest)

    被动使用

    除了上面的几种主动使用其余就是被动使用了。

    1. 引用该类的静态常量,不会导致初始化,但是也有意外,这里的常量是指已经指定字面量的常量,对于那些需要一些计算才能得出结果的常量就会导致初始化。

      public final static int NUMBER = 5 ; //不会导致类初始化,被动使用
      public final static int RANDOM = new Random().nextInt() ;//会导致类的初 始化,主动使用
      
    2. 构造某个类的数组时不会导致该类的初始化。

      Student[] students = new Student[10] ;
      

    注意:主动使用和被动使用的区别在于类是否会被初始化。

    7.类装载方式

    面试题:
    描述一下JVM加载Class文件的原理机制
    

    java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的,除非我们有特殊的用法,像是反射,就需要显示的加载所需要的类。

    类装载方式:

    1. 隐式装载,程序在运行过程中当碰到通过new等方式生成对象时,隐式调用类装载器加载对应的类到jvm中。
    2. 显式装载,通过class.forName()等方法,显式加载需要的类。

    java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类完全加载到JVM中,至于其他类,则在需要的时候才加载。节省内存开销。

    面试题:
    在jvm中如何判断两个对象是属于同一个类?
    
    1.类的全类名(地址)完全一致。
    2.类的加载器必须相同。
    
    展开全文
  • JVM类加载器详解

    2020-12-08 20:33:55
    本文主要针对加载器进行讲解 类加载器ClassLoader 负责加载class文件,class文件在文件开头有特定的文件标示,并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。 加载...
  • JVM中有两种类型的类加载器,由C++编写的及由Java编写的。除了启动类加载器(Bootstrap Class Loader)是由C++编写的,其他都是由Java编写的。由Java编写的类加载器都继承自java.lang.ClassLoader。 JVM还支持...
  • 在上一篇JVM类加载机制详解(一)JVM类加载过程中说到,类加载机制的第一个阶段加载做的工作有: 1、通过一个的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件)。而获取的方式,可以通过jar包...
  • Java 通过引入字节码和 JVM 机制,提供了强大的跨平台能力,理解 Java 的类加载机制是深入 Java 开发的必要条件。
  • JVM-类加载详解

    2022-03-19 20:08:37
    一、JVM类加载过程 JVM类加载过程如下图: JVM类加载过程分为:加载 、链接 、初始化 、使用 、卸载 这五个阶段,其中链接阶段又包括: 验证 、 准备 、 解析 。 加载 :通过的完全限定名,查找此类的二进制...
  • jvm的启动是通过引导类加载器(bootstrap class loader)创建一个初始(initial class)来完成的,这个是由jvm的具体实现指定的。[来自官方规范]jvm组成结构之一就是装载器子系统,我们今天就来仔细讲讲这个组件。...
  • jvm的启动是通过引导类加载器(bootstrap class loader)创建一个初始(initial class)来完成的,这个是由jvm的具体实现指定的。[来自官方规范]jvm组成结构之一就是装载器子系统,我们今天就来仔细讲讲这个组件。...
  • 受多种情况的影响,又开始看JVM 方面的知识。...JVM-类加载器 详解一、概述二、类加载器的分类2.1、Bootstrap ClassLoader(根类加载器)2.2、Extension ClassLoader(扩展类加载器)2.3、AppClassLoader (应用程.
  • jvm之java加载机制和类加载器(ClassLoader)的详解

    万次阅读 多人点赞 2018-08-13 15:05:46
    当程序主动使用某个时,如果该还未被加载到内存中,则JVM会通过加载、连接、初始化3个步骤来对该进行初始化。如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载初始化。 一...
  • JVM类加载阶段详解

    2022-01-11 22:42:18
    JVM之类加载阶段详解类加载阶段总览加载获取二进制流将字节流转换为运行时数据结构堆中生成Class对象特殊连接验证准备解析名词解释何时进行解析哪些类型初始化使用卸载 类加载阶段总览 注意:这些阶段的顺序虽然是...
  • 主要介绍了JVM加载过程以及双亲委派模型详解,类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。,需要的朋友可以参考下
  • 但是虚拟机团队将加载阶段第一步中的”通过一个的全限定名来获取描述该的二进制字节流”这个动作放到虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的...,实现这个动作的代码模块被称为”类加载器”...
  • Java高并发编程详解系列-JVM类加载器

    千次阅读 2019-05-12 18:29:01
    之前的博客中提到了加载的过程,提到了双亲委托机制,提到了关于类加载器的概念,这篇博客就来给大家分享一下什么是JVM类加载器。通过实战的方式来了解一下类加载器器到底是什么。 JVM类加载器分类   ...
  • JVM类加载机制 Java程序运行过程 首先通过Javac编译器将==.java转为JVM加载的.class==字节码文件。 javac是由Java编写的程序,编译过程可分为 词法分析。通过空格分割出单词、操作符、控制符等信息,形成token...
  • JVM系列】类加载器详解(JDK9+)

    千次阅读 2020-10-27 00:11:48
    无名模块(Unnamed Module):不分模块的 jar 包,放到 不分模块的路径(即这个项目路径下) 无名模块指的就是不包含 module-info.java 的 jar 包,通常这些 jar 包都是 Java 9 之前构建的。无名模块可以读取
  • 本文参考自: 原文地址 类加载器,主要负责将字节码文件(.class文件)加载到内存中。...另一种是所有其他类加载器,它们由Java语言实现,独立于JVM虚拟机外部,并且全部继承自抽象Java.lang.C...
  • JVM类加载机制详解

    2021-03-17 20:52:50
    一、先看看编写出的代码的执行过程:二、研究类加载机制的意义从上图可以看出,类加载是Java程序运行的第一步,研究加载有助于了解JVM执行过程,并指导开发者采取更有效的措施配合程序执行。研究类加载机制的第...
  • Java虚拟机把描述的数据从Class文件加载到内存, 并对数据进行校验、 转换解析和初始化, 最终形成可以被虚拟机直接使用的Java类型, 这个过程被称作虚拟机的类加载机制。 Class文件被加载到内存中是如何存储的?...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 27,237
精华内容 10,894
关键字:

jvm类加载器详解