JVM学习笔记(6)-类加载机制

1.类的生命周期

虚拟机吧描述类的数据从Class文件加载到内存,并对数据进行校验、转换、解析、初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制。


与其他在编译时进行连接工作的语言不同,java语言中,类型的加载、连接、初始化都是在程序运行期间完成的,故java可以动态加载和动态连接的特点



类从被加载到虚拟机内存中开始,到卸载为止,整个生命周期包括:加载、连接、初始化、使用、卸载等过程;连接过程有三个部分:验证、准备、解析

加载、验证、准备、初始化、卸载这五个部分的顺序是确定,类加载过程必须按照这个顺序按部就班的执行,而解析阶段则不一定;在某些情况下解析可以在初始化阶段之后在开始,这是为了支持java语言的运行时绑定(动态绑定)


(1)加载

装载类的第一阶段,取得类的二进制流(class文件),转化为方法区的数据结构,在java堆中生成对应的java.lang.Class对象

(2)连接(分三个阶段:验证、准备、解析)

【1】验证:

保证class类格式的正确,如魔数验证、版本号验证等;

源数据验证,如是否有父类、是否继承final类、非抽象类是否实现了所有抽象方法;

字节码验证:运行检查,如栈数据和操作数参数是否吻合、跳转指令是否指到合理位置

符号引用验证:常量池中描述的类是否存在,访问字段或方法是否有权限

【2】准备:

分配内存,并未类设置初始值(方法区中)

如:public static int v=1;在准备阶段被设置为0,在初始化<clinit>时被设置为1

如果改为public static final int v=1;则在准备阶段就直接赋值1;

【3】解析:

符号引用替换成直接引用

符号引用是指字符串引用对象不一定被加载;直接引用是指指针或地址偏移量,引用对象一定在内存中

(3)初始化

执行类构造器<clinit>

static变量赋值

static块执行

子类<clinit>执行前保证父类的<clinit>被调用

<clinit>是线程安全的


2.类在什么时候初始化

在初始化之前必须先进行加载、验证、准备阶段

在下列5中情况中必须立即进行初始化

(1)遇到new、getstatic、putstatic、invokestatic这四条字节码指令时,如果类没有进行过初始化,则需先触发其初始化

使用new 关键字实例化对象的时候

读取或设置一个类的静态字段(被final修饰,在编译器把结果放入常量池的静态字段除外)

调用一个类的静态方法的时候

(2)使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需触发其初始化

(3)当初始化一个类的时候,如果父类没有进行过初始化,则需触发其父类初始化

(4)虚拟机启动时,用户需要指定一个要执行的主类,含有main方法的那个类,虚拟机会先初始化这个主类

(5)当使用jdk7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果是REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法的句柄所对应的类没有进行初始化,则需先触发其进行初始化


【1】通过子类引用父类的静态字段,不会导致子类初始化。对于静态字段,只有直接定义这个字段的类才会被初始化





【2】通过数组定义定义表引用类,不会触发此类的初始化

【3】被static final 修饰的常量在编译期间就放入到了常量池中,本质上是没有直接引用到定义常亮的类,不会触发其初始化

在testNotInitialization.java的编译期就将ConstClass类的HELLO变量值hello放到了testNotInitialization类的常量池中





【4】当一个类在初始化时需要求其父类已经全部初始化,而接口则不要求这样,只要在真正使用到父接口的时候才会初始化


注意:

类方法中的局部变量是不存在“准备”阶段的;而类变量则由两次赋值的过程,一次是在“准备阶段”赋值为系统初值,一次是在初始化阶段赋值为程序员设置的初始值


3.类加载器ClassLoader

ClassLoader是一个抽象类;

ClassLoader实例将java字节码加载到JVM中

ClassLoader可以定制不同字节码流的获取方式

ClassLoader负载类装载过程中的加载阶段


3.双亲委派模型

启动类类加载器:Bootstrap ClassLoader,C++语言编写,作用是加载rt.jar,可以使用-Xbootclasspath设置启动类加载器加载的路径

扩展类加载器:Extension ClassLoader,负责加载JAVA_HOME\lib\ext目录中的jar

应用程序加载器:Application Classloader,负责加载用户路径下classpath的类


双亲委派模型工作过程:

如果一个类加载器收到类加载的请求,首先不会自己尝试去加载这个类,而是把这个请求委派给父类加载器去完成(注意,启动类加载器不是任何类加载器的父类加载器),所有的加载请求都会传送到顶层的启动类加载器中,只要当父类加载器无法加载的时候(他的搜索范围没有找到该类)时,子加载器才会尝试自己去加载。


双亲委派模型的好处:

比如自己写的Object类是永远不会被加载的,该类的加载请求会传到顶层加载器加载的是rt.jar中的Object.class,避免了类被覆盖


双亲委派模型也不是一成不变的:

可以使用反射的方式使用特定的类加载器去加载某个类

也可以使用Thread.setContextClassLoader()方法进行设置


tomcat中WebappClassLoader会加载自身的类,而不会委派给其父类
osgi中ClassLoader形成网状结构,根据需要由特定的Bundle加载器加载,每个bundle都有自己的类加载器



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值