• JVM系列(二):Java类的加载机制


    一、概念

    1.  字节码:

    a. 以前的代码(例如C++)编译后是本地机器码,不同的机器编译出来的机器码是不同的

    b. Java编译后是相同的字节码文件,即存放在.class文件中的二进制文件,JVM可以执行任何符合规范的字节码文件

    2. 类加载机制:将描述类的二进制数据从.class文件读入内存的不同区域中,并对数据进行校验、转换、解析和初始化,最终形成可以让JVM直接使用的Java类型

    3. 类加载结果:在堆区创建了一个java.lang.Class对象,用来封装类在方法区内的数据结构

    4. 允许预加载

    5. 可控性强,可用自定义的类加载器完成加载

    二、加载.class文件的方式

    1. 从本地系统中直接加载

    2. 通过网络下载.class文件

    3. 从zip,jar等归档文件中加载.class文件

    4. 从专有数据库中提取.class文件

    5. 将Java源文件动态编译为.class文件

    三、类的生命周期:加载->(验证->准备->解析)链接->初始化->使用->卸载

    1. 加载,如上所述

    2. 链接

    a. 验证:确保被加载的类的正确性

    b. 准备:为类的静态变量分配内存,而不是实例变量;并初始化为数据类型的默认值,而不是代码中赋予的值;为static final赋代码中的值

    c. 解析:把类中的符号引用改为直接引用

    3. 初始化:为静态变量初始化实际的值,JVM对类和类变量初始化

    4. 卸载

    四、类加载器ClassLoader:通过一个类的全限定名来获取描述此类的二进制字节流,这个动作在JVM外部实现,以便让程序自己决定如何去获取所需的类

    1. 启动类加载器,BootStrap ClassLoader

    a. 由C++实现,是虚拟机的一部分,其他类加载器都由Java实现,以便让程序自己决定如何去获取所需的类

    b. 加载JAVA_HOME/lib目录中的类(JAVA_HOME指明JDK安装路径),或者-Xbootclasspath参数所指定的路径中的类,并且被JVM按照文件名识别的,例如JRE/lib/rt.jar

    2. 扩展类加载器,Extension ClassLoader,加载JAVA_HOME/lib/ext目录下的类,或者被java.ext.dirs系统变量所指定的路径下的类。关键词:ext

    3. 应用程序类加载器,Application ClassLoader

    a. 程序中默认的类加载器,除非自定义了类加载器

    b. 加载用户路径(ClassPath)上的类

    c. 是ClassLoader中,getSystemClassLoader方法的返回值

    4. 用户自定义类加载器,User ClassLoader,加载编程人员制定的目录下的类

    5. 从JVM的角度来看,类加载器分为两类:BootStrap类加载器(JVM里的C++实现)、其他类加载器(外部Java实现)。

    6. 从开发人员角度,分为3类:启动类加载器、扩展类加载器、应用类加载器

    五、类与类加载器的关系

    1. 类加载器虽然只用于实现类的加载动作,但它在Java程序中的作用,却远远不限于类加载阶段

    2. 类本身+加载这个类的类加载器=Java虚拟机中的唯一性,用于比较类是否相等

    六、类的加载方式

    1. 命令行启动应用时,由JVM初始化加载

    2. 通过Class.forName()方法动态加载:将.class文件加载到JVM中,执行类中的static块

    3. 通过ClassLoader.loadClass()方法动态加载,只加载.class文件

    七、双亲委派模型

    1. 如果一个类加载器收到了类加载的请求,它首先把请求委托给父加载器去完成,依次向上,只有父加载器无法完成时,才由子加载器完成。

    2. 上述过程完成后,如果AppClassLoader加载失败,会抛出ClassNotFoundException

    3. 作用:解决了基础类的统一问题,防止内存中出现重复的字节码,保证Java程序安全执行。例如用户编写了java.lang.Object类,并放在ClassPath中,系统中会有多个Object类,就混乱了。

    4. 类加载器间的父子关系不是继承,而是组合

    5. 实现过程:即java.lang.ClassLoader中的loadClass()方法,递归调用父加载器,最终是findClass()方法完成加载操作

    6. 自己写一个类加载器去加载java.lang.Object或者java.lang.String类

    八、破坏双亲委派模型

    1. 双亲委派模型不是强制要求,而是建议性的类加载机制

    2. 双亲委派模型的缺点:一般情况下,基础类是被用户代码调用的;特殊情况下,基础类会调用用户的代码,例如JNDI服务

    3. 线程上线文类加载器(Thread Context ClassLoader),可以通过java.lang.Thread类中的setContextClassLoader()方法设置,父加载器请求子加载器去完成加载

    4. 破坏双亲委派机制的场景:

    a. 涉及SPI的加载动作,例如JNDI、JDBC、JBI

    b. 热拔插、热部署、模块化,增减模块时不需要重启,只需要把此模块和类加载器一起去掉

    九、自定义类加载器

    1. 应用场景:用网络传输加密的字节码

    2. 使用方法:继承ClassLoader类,并重写findClass()方法

  • 相关阅读:
    十天学会php之第一天
    学习PHP的一些经验
    PHP中的数据类型(1)
    PHP中的常量
    赵凡导师并发知识第一次分享观后感
    面向对象之 __setitem__()、__getitem__()、__delitem__() 用法
    spider数据抓取(第二章)
    识别网站所用技术
    scrapy安装要求
    基于bs4的防止xss攻击,过滤script标签
  • 原文地址:https://www.cnblogs.com/june0816/p/8011570.html
Copyright © 2020-2023  润新知