JVM--类加载机制

对类加载机制的学习(2023.3.10改)
对类加载模块的学习,要理解以下几个部分:class文件结构,类加载生命周期,类加载机制层次

1、字节码文件结构

对于java语言(或者其他基于jvm的语言,如scala)的编译运行过程为
java语言----->编辑器-------->字节码文件
字节码文件----->jvm ------->机器码
所谓字节码文件本质上是一种二进制流,jvm根据自己的规则来解析这个二进制文件

首先的cafebabe成为魔数,表示这个文件是字节码文件
class文件除了类的版本、字段、方法和接口等描述信息外,还有一个信息是常量池,用于存放编译期间生成的各种字面量和符号引用,字面量包含字符串(String a="b")和基本类型的常用(直接赋值的变量,比如int a=1),而符号引用包括了类的全限定名,方法和字段的名称、描述符等。

2、类加载生命周期

类加载过程分为五个部分:加载,验证,准备,解析,初始化

加载

加载阶段就是将Class文件的内容放入方法区中,这句话不同版本的JDK有不同的实现,比如JDK1.7及以前,方法区是由JVM内存(准确来说JVM Heap)的永久代实现,但是JDK8之后,方法区分成了两个部分,第一个部分依旧在JVM内存中,即运行时常量池,这个保存Class文件的常量池(也叫类常量池和Class常量池)中的字面量和符号引用,第二个部分则是由元空间实现,这部分属于操作系统直接管理的内存空间,不属于JVM,这里存放Class文件中类的基本信息。

验证

验证,这个阶段的工作是确保class文件的子节流中包含的信息符合当前虚拟机要求,并且不会危害虚拟机

准备

准备阶段是对静态变量分配内存,并且初始为默认值
Static变量也叫类变量,与类的生命周期保持一致,有两种赋值方法

  1. 在构造方法中赋值,也就是clinit方法
  2. 如果是static+final修饰的基本变量和String变量则使用ConstantValue赋值

对应着有两种时刻,第一种在初始化阶段赋值,第二种则在准备阶段就赋值了,但这是HotSpot VM的实现,如果是JVM规范中都是在初始化阶段赋值的(也不用太在意)

解析

解析阶段将类中的符号引用转换为直接引用.

初始化

初始化指得就是对类中的静态变量赋值真正的初值,这个过程就是类构造器clinit方法的执行过程,所谓clinit方法就是收集了代码中静态代码块和静态方法

3、类加载器机制

  • Bootstrap加载器加载JAVA_HOME/jre中的类,Extension加载类加载JAVA_HOME/jre/ext,Application加载器负责加载用户路径上的类,其中启动类加载器是cpp写的,其余的是java写的,如果想要自定义加载器,那么就继承ClassLoader类,并重写findClass方法

  • 其次,类加载的方式也分为三种
    第一种就是静态加载,就是通过new关键字创建对象,第二种就是通过class.forName动态加载,第三种就是classloader.loadClass动态加载,动态加载要通过newInstance方法创建对象
    classLoader.loadClass(),他的源码流程是

    resolve是解析的意思,默认是false

    这里紫色的框是双亲委派的关键代码,可以看出直接抛给上一层加载,如果最后没有则执行自定义的findClass方法加载
    红色框的代码为

    这个方法有这样的注释,说明loadClass默认只执行第一步"加载",后面的连接(验证,准备,解析)是可以选择的

接下来看forName的源码

关键看initialize参数,这个参数表示初始化,默认是进行初始化的

因此,loadClass和forname最大的区别是loadClass只负责加载,forname是要进行初始化的,foname常用在jdbc连接mysql时,而loadClass可以避免初始化对象,可以选择在使用该类对象时再初始化.
此外,也该注意new和newInstance的区别,new是静态加载,而newInstance是动态加载,需要保证类已经加载并已经建立连接,newInstance有两种用法Class.newInstance(),这个Class对象只能执行无参构造,另一种需要获得含参的构造器,如Construct cs=Dog.class.getConstructor(String.class)//获取含有一个String类型参数的构造器,然后执行cs.newInstance(“小明”)

  • 类加载方式叫做双亲委派机制,也就是说优先委托较高层次的加载器加载,这样的目的是为了重复加载类,如果想破坏双亲委派机制,那么自定义加载器的时候,就不是重写findClass方法而是重写loadClass方法
posted @ 2022-11-11 22:31  不要给我歪!  阅读(27)  评论(0编辑  收藏  举报