读《深入理解Java虚拟机》- 类加载过程与类加载器

类加载过程:
加载---验证---准备---解析---初始化

虚拟机规范规定只有以下5种情况会初始化类:
1、遇到new getstatic putstatic invokestatic这四条字节码指令时,如果类没有进行过初始化就初始化类
new:就是普通的new一个类的时候
getstatic:就是读取一个类的静态变量时(被final修饰、已经在编译器把结果放入常量的静态变量除外)
putstatic:给一个类的静态变量赋值时
invokestatic:调用一个静态方法时
2、当对类进行反射调用的时候,如果类还没有初始化就初始化类
3、当初始化类的时候,发现其父类还没有初始化就先初始化父类
4、当虚拟机启动的时候,要指定一个执行的主类main,虚拟机会先初始化这个主类
5、当使用jdk1.7以上的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果是REF_getStatic、REF_putStatic、REF_invokeStaic的方法句柄,并且这个方法的句柄所对应的类没有进行过初始化,则需要先初始化这个类

上面5中场景的行为成为对一个类进行主动引用,除此之外所有引用类的方式都不会触发类的初始化
被动引用举例说明如下:
1、通过子类调用父类的静态字段,不会导致子类初始化(上面第1类:此时父类若没有初始化则初始化)
2、如果通过数组定义来引用类,则不会触发此类的初始化(SuperClass[] sca = new SuperClass[10];)
3、常量在编译阶段就会进入常量池,本质上并没有引用到定义常量的类,因此不会触发定义常量的类的初始化

下面正式说明类加载过程做了啥:
(1)加载:
1、通过一个类的全限定名来获取类对应的二进制字节流
2、把字节流代表的静态存储结构转换为方法区的运行时数据结构
3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区的这个类的各种数据的访问入口
(2)验证:
目的是确保Class文件的字节流中包含的信息符合虚拟机规范(因为class文件不一定是Java源码编译来的,上述加载过程的第一个说明了来源有很多)
1、文件格式验证
2、元数据验证:语义分析,对数据类型做校验
3、字节码验证:通过数据流和控制流分析,确定程序语义是合法,符合逻辑的。对类的方法体进行校验分析
4、符号引用验证:发生在解析阶段将符号引用转换成直接引用的过程。其实就是通过类的全限定名,字段名,方法名能不能找到对应的类、字段、方法
(3)准备:
为类变量(static修饰)分配内存和初始化零值(零值:0,null,false等),实例变量是在对象实例化的时候随着对象一起分配到堆中
public static int a = 12;准备阶段a的初值是0
把变量赋值为12的阶段是在执行putstatic指令后,也就是在类的初始化阶段
如果变量存在常量值的属性,那就会在准备阶段把值12赋给a。public final static int a = 12;
(4)解析:
虚拟机将常量池内的符号引用替换为直接引用的过程。
符号引用是以一组符号来描述所引用的目标,理解为引用的一个标识符,它和内存布局无关。逻辑指针?
直接引用是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄,和内存布局相关。物理指针?
符号引用一般会解析很多次,虚拟机实现了第一次对解析结果缓存(在运行时常量池记录直接引用,并把常量标识为已解析),下次解析就直接成功
类或接口解析、字段解析、类方法解析、接口方法解析、
(5)初始化:
真正开始执行类中定义的Java程序代码,也就是执行类构造器<clinit>()方法,<clinit>()会收集类中定义的类变量的赋值语句和静态代码块static{}块
static{}块只能访问定义在它之前的变量
虚拟机会保证父类的<clinit>在子类之前完成,不需要显示的调用类的构造器<init>
<clinit>不是必须的,因为一个类中要是没有定义静态变量或静态块就没有这个东西
接口中不能使用静态语句块,接口与类不一样的一点在于执行接口的<clinit>不需要先执行父类的<clinit>,父类在用到时才初始化,另外接口的实现类也一样不会执行接口的<clinit>
多个线程去执行<clinit>,只有一个线程能执行,其他线程阻塞


类加载器:
通过一个类的全限定名来获取此类的二进制字节流的动作就是类加载器实现的。
看两个类是否相等一定要看两个类是否由同一类加载器加载的,然后再看他们本身的属性是否相等,例如在用instanceof时,就算是同一个类,但是由不同的类加载器加载出来的就返回false
双亲委派模型:
从父到子的顺序如下:
启动类加载器:Bootstrap ClassLoader,加载<JAVA_HOME>\lib目录中的类
扩展类加载器: Extension ClassLoader,加载<JAVA_HOME>\lib\ext目录中的类
应用程序类加载器:Application ClassLoader,加载classpath,这里相当于就算加载自己写的程序的类加载器,在没有自定义类加载器的情况下

过程:当一个类加载器收到一个加载类的请求时,首先去找父类加载器,父类又找父类的,一直到最顶层(Bootstrap ClassLoader),父类要是找到了就加载,没找到就传导下去让它的子类加载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值