JVM-类加载机制

本文详细解析了Java类加载机制,包括.class文件结构、javap命令的使用、加载、验证、准备、解析和初始化过程,以及双亲委派模型的工作原理。还探讨了为何需要双亲委派、打破双亲委派模式的原因以及Tomcat如何实现类加载的隔离和共享性。
摘要由CSDN通过智能技术生成
名词解释
*.class文件的结构
查看指令: javap -verbose hello.class
包含信息:

结构信息(版本号,大小信息);

元数据(类,继承,接口,字段声明,方法声明);

方法信息(语句,表达式,异常,堆栈大小与类型,调试信息)

符号引用
符号引用符号引用就是 class 文件中的     
1. CONSTANT_Class_info 
2. CONSTANT_Field_info
3. CONSTANT_Method_info等类型的常量
符号引用以 一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可,使用符号引用时,被引用的目标不一定已经加载到内存中。
直接引用
直接引用可以是指向目标的指针,相对偏移量或是一个能间接 定位到目标的句柄
如果有 了直接引用,那引用的目标必定已经在内存中存在

参考:

个人理解,如果使用符号引用,虚拟机其实也不知道具体引用的类的内存地址,那么也就无法真正的调用到该类,所以要把符号引用转为直接引用,这样就能够真正定位到类在内存中的地址,如果符号引用转直接引用失败,就说明类还没有被加载到内存中,就会报错。

https://blog.csdn.net/weixin_38106322/article/details/109239971

类加载机制概述

1、类加载器把编译好的那些“.class”字节码文件给加载到JVM中,供后续代码运行来使用。
2、JVM基于自己的 字节码执行引擎,来执行加载到内存里的类。
3、类加载的时机
   一个类被使用到到时候
eg:比如你的代码中有一个“main()”方法,那么JVM就会从这个“main()”方法开始执行里面的代码。他需要哪个类的时候,就会使用类加载器来加载对应的类,反正对应的类就在“.class”文件中。
4、通过Java命令执行代码的大体流程如下:
java  xxx 指令 在Windows下对应就是一个exe文件。dll 相当于java jar包。
C++启动程序 -》创建java虚拟机 -》java类加载器 -》加载class文件

类加载机制详解

类是如何加载到jvm中
JVM 类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程。

加载过程分析

1. 加载

通过类全名来获取定义此类的二进制字节流,
将字节流所代表的 静态存储结构转换为方法区中运行时数据结构
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
扩展:
  • 静态存储结构:数据存储于class文件的结构。
  • 运行时数据结构:数据存储于JVM的数据结构。
  • 二进制流是从.class文件中获取的。这个class文件并不一定只来源于本地,也可以来源于网络等。

2. 连接

将java二进制代码合并到java运行状态中的过程。
  • 验证:校验字节码文件的正确性 目的保证class流的格式正确。
(简单来说,这一步就是根据Java虚拟机规范,来校验你加载进来的“.class”文件中的内容,是否符合指定的规范。)
  • 准备:Java虚拟机为类变量(static修饰的变量)、加载到内存中的类分配内存空间,设置默认初始值。
    •   示例代码:
public static int v=1; 在准备阶段中,v会被设置为0   在初始化的<clinit>中才会被设置为1
对于static final类型,在准备阶段就会被赋上正确的值: public static final  int v=1;
  • 解析::将符号引用替换为直接引用
    • 该阶段会把一些静态方法(符号引用,比如 main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过 程(类加载期间完成),
    • 动态链接:是在程序运行期间完成的将符号引用替换为直接引用
3  初始化 (核心阶段)
初始化阶段是类加载最后一个阶段,前面的类加载阶段之后,除了在加载阶段可以自定义类加载器以外,其它操作都由 JVM 主导。到了初始阶段,才开始真正执行类中定义的 Java 程序代码。如: 在初始化阶段,JVM执行类的初始化语句, 为类的静态变量赋予用户给定的初始值,执行静态代码块 
什么时候会初始化一个类?
一般来说有以下一些时机:比如“new ReplicaManager()”来实例化类的对象了,此时就会触发类的加载到初始化的全过程,把这个类准备好,然后再实例化一个对象出来;(这里也说明了为什么 构造方法是在 静态代码块之后执行(先准备代码在实例化 ))
或者是包含“main()”方法的主类,必须是立马初始化的。
此外,这里还有一个非常重要的规则,就是如果初始化一个类的时候,发现他的父类还没初始化,那么必须先初始化他的父类

4 使用

5 卸载

加载器的类型

类加载器类型总体来说可以分为以下几种:启动类加载器、其他类加载器。类的加载过程遵循双亲委派模型。
1、Bootstrap ClassLoader(启动类加载器)
    JVM启动,启动类加载器 加载 Java安装目录下jre 的 “lib”目录中的核心类库
/opt/apps/jdk1.8.0_251/jre/lib
--> rt.jar、charsets.jar等> rt.jar、charsets.jar等
2、Extension ClassLoader(扩展类加载器)
    JVM启动,通过扩展类加载器加载“lib/ext”目录
[root@iZ2zei4wx78lgr80odfj6eZ ext]# pwd# pwd
/opt/apps/jdk1.8.0_251/jre/lib/ext
3、Application ClassLoader(应用程序类加载器)
    加载ClassPath 环境变量所指定的路径中的类
4、自定义类加载器    
    根据自己需求进行类加载

类加载器初始化过程

参见类运行加载全过程图可知其中会创建JVM启动器实例sun.misc.Launcher。 
sun.misc.Launcher初始化使用了单例模式设计,保证一个JVM虚拟机内只有一个 sun.misc.Launcher实例。 
在Launcher构造方法内部,其创建了两个类加载器,分别是 sun.misc.Launcher.ExtClassLoader(扩展类加载器)和sun.misc.Launcher.AppClassLoader(应 用类加载器)。 
JVM默认使用Launcher的getClassLoader()方法返回的类加载器AppClassLoader的实例加载我们 的应用程序。

双亲委派模型

概述(向上寻找、向下委托)

类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是 把这个请求委派给父类加载器完成。
每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),
子加载器才会尝试自己去加载,这个过程为称为双亲委派模型。

AppClassLoader加载类的双亲委派机制源码

AppClassLoader 的loadClass方法最终会调用其父类ClassLoader的loadClass方法,
  • 1. 首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接 返回。 
  • 2. 如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加 载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加 载。
  • 3. 如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的 findClass方法来完成类加载。 
  • 对应代码位置://ClassLoader的loadClass方法,里面实现了双亲委派机制

为什么需要双亲委派模型

  • 保证类加载机制的安全性。
    • 防止病毒代码植入,自己写的java.lang.String.class类不会被加载,这样便可以防止核心 API库被随意篡改
  • 避免类的重复加载:
    • 当父亲已经加载了该类时,就没有必要子ClassLoader再加载一 次,保证被加载类的唯一性
  • 参考 : https://www.cnblogs.com/wxd0108/p/6681618.html

全盘负责委托机制 

  • “全盘负责”是指当一个ClassLoder装载一个类时,除非显示的使用另外一个ClassLoder,该类 所依赖及引用的类也由这个ClassLoder载入。

如何打破双亲委派模式

  • 覆写loadClass方法

打破双亲委派模式的案例

  • tomcat类加载器
    • Common类加载器,加载Tomcat和Web应用都复用的类。
    • Catalina类加载器,加载Tomcat专有类(这些类在web应用中不可见)
    • Shared类加载器,加载所有Web应用都复用都类。(这些类tomcat中不可见)
    • WebappClassLoader:webapp私有的类加载器,只对当前webapp可见
  • Jsp类加载器,不同的jsp页面有不同的类加载器,方便实现jsp页面的热插拔。
  • 类加载机制原理
    • a. CommonClassLoader能加载的类都可以被Catalina ClassLoader和SharedClassLoader使用,从而实现了公有类库的共用

      b. CatalinaClassLoader和Shared ClassLoader自己能加载的类则与对方相互隔离

      c. WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离,多个WebAppClassLoader是同级关系

      d. 而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能

  • 为什么打破双亲委派模式?
    • 隔离性
      • 一个web应用可以部署多个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本。
        • 因此需要保证每个应用程序的类库是独立的、相互隔离的。
      • web容器也有自己的类库,不能和应用程序的类库混淆,需要相互隔离
    • 共享性
      • 部署在同一个web容器中的相同类库的相同版本可以共享,否则,会有重复的类库被加载进JVM
    • 独立性
      • web容器支持jsp文件修改后不用重启,jsp文件也是要编译成.class文件的,支持HotSwap功能
  • tomcat如何打破双亲委派模式的?
    • 参考tomcat类加载机制 
  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码匠心印记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值