• JVM类加载器


    什么是JVM类加载器?

    类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。

    一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。

    类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。

    每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。

    实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。

    类加载的过程

    从类的生命周期而言,一个类包括如下阶段:

     

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

    加载过程:将.class文件通过IO流的方式加载到内存当中

    • 1.将.class文件字节码内容加载到内存当中
    • 2.先会将静态数据转换成方法区中的运行的数据结构
    • 3.在堆内存当中生成一个代表这个类的Class对象,这个Class类的对象就是作为方法区数据访问的入口 Class.forName(com.wdksoft.User);

    链接过程:

    • 1.验证阶段:验证字节码文件的准确性,包含文件格式,元数据,符号引用,字节码等等
    • 2.准备阶段:给类中的静态变量分配内存,并赋予初始值
    • 3.解析阶段:将虚拟机常量池的符号引用替换成字节引用的过程

    初始化过程:

    • 初始化过程就会对类中的静态变量初始化为指定的值,执行静态代码块,执行构造器

    类加载时机

      加载(loading)阶段,java虚拟机规范中没有进行约束,但初始化阶段,java虚拟机严格规定了有且只有如下5种情况必须立即进行初始化(初始化前,必须经过加载、验证、准备阶段):

    •     (1)使用new实例化对象时,读取和设置类的静态变量、静态非字面值常量(静态字面值常量除外)时,调用静态方法时。
    •     (2)对内进行反射调用时。
    •     (3)当初始化一个类时,如果父类没有进行初始化,需要先初始化父类。
    •     (4)启动程序所使用的main方法所在类
    •     (5)当使用1.7的动态语音支持时。

     如上5种场景又被称为主动引用,除此之外的引用称为被动引用,被动引用有如下3种常见情况

    • 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
    • 定义对象数组和集合,不会触发该类的初始化
    • 类A引用类B的static final常量不会导致类B初始化(注意静态常量必须是字面值常量,否则还是会触发B的初始化)

     注意:被动引用不会导致类初始化,但不代表类不会经历加载、验证、准备阶段。

    类加载器的种类

    Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。系统提供的类加载器主要有下面三个:

    • 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader
    • 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
    • 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

    除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。

    类加载器树状组织结构示意图

    类加载方式

    这里的类加载不是指类加载阶段,而是指整个类加载过程,即类加载阶段到初始化完成。

    (1)隐式加载

    • 创建类对象
    • 使用类的静态域
    • 创建子类对象
    • 使用子类的静态域
    • 在JVM启动时,BootStrapLoader会加载一些JVM自身运行所需的class
    • 在JVM启动时,ExtClassLoader会加载指定目录下一些特殊的class
    • 在JVM启动时,AppClassLoader会加载classpath路径下的class,以及main函数所在的类的class文件

    (2)显式加载

    • ClassLoader.loadClass(className),只加载和连接、不会进行初始化
    • Class.forName(String name, boolean initialize,ClassLoader loader); 使用loader进行加载和连接,根据参数initialize决定是否初始化。

    类加载机制

    1.全盘负责委托机制
    当进行类加载的时候,如果手动指定了ClassLoader,那么该类所依赖和引用的类也由这个类加载器进行加载
    User->UserParent
    指定User使用特定的类加载器,那么跟User类有依赖和引用关系的类也用这个类加载器进行加载
    2.双亲委派机制
    指先委托父类加载器寻找目标类,如果父类加载器无法进行类的加载则子类加载器自身处理

    • 1.沙箱安全机制:自定义的String.class不会被加载,这样可以防止核心API库被随意篡改
    • 2.避免类重复加载:当附加在其加载了该类是,就没有必要子类加载器也进行加载

    3.如何破坏双亲委派机制
    为什么要破坏双亲委派机制:父加载器需要委托子加载器在其进行加载
    如何破坏:

    • 1.重写ClassLoad类中的loadClass方法,指定加载哪一个类
    • 2.手动调用系统类加载器Thread.currentThread().getContextClassLoader();
    • 3.重写findClass

    监控类加载过程

     在当前启动类当中加入-verbose:class参数,启动则可以看到整个类加载的过程

    代码

    package com.wish;
    
    public class ClassLoaderTest {
        private static Integer i=50;
    
        static {
            System.out.println("静态代码块");
            i=70;
        }
    
        public ClassLoaderTest(){
            System.out.println(i);
            System.out.println("类中构造函数");
        }
    
        public static void main(String[] args) {
            ClassLoaderTest test = new ClassLoaderTest();
        }
    }
    

      

    参数

     结果

    博客引用:https://blog.csdn.net/zhaocuit/article/details/93038538

         https://www.ibm.com/developerworks/cn/java/j-lo-classloader/

  • 相关阅读:
    Lua/AHK socket tcp telnet
    Lua wait sleep
    Lua io.open read write seek flush setvbuf append
    stream file 文件 数据流
    AHK通讯 CMD Lua IPC
    零散 Lua/Excel/''/iup
    Windows Program File(x86) 路径 环境变量
    条件正则过滤筛选 V2
    条件正则过滤筛选 V1
    python导包出现的问题
  • 原文地址:https://www.cnblogs.com/wishsaber/p/12482108.html
Copyright © 2020-2023  润新知