《深入理解Java虚拟机》学习笔记
类加载的时机
有且只有5种情况下:
- 使用new实例化对象,读取或者设置一个静态变量(
final
修饰的静态变量除外,因为它已经在编译期被放到了常量池中了) - 反射触发
- 初始化一个类时,父类会执行类加载
- 虚拟机启动时,主类会执行类加载
- 方法句柄,
MethodHandle
类的生命周期
image-20190501090804120类加载的过程
加载
三个步骤:
- 通过一个类的全限定名来获取定义此类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成一个代表这个类的
Class
对象,作为方法区这个类的各种数据入口
验证
确保class文件中的字节流中包含的信息是否符合虚拟机的要求
准备
正式为类变量分配内存并设置类变量初始值
-
public static int value = 123
- 变量
value
准备阶段过后值为0,因为这时候尚未开始执行任何java方法
- 变量
-
public static final int valve
- 变量
valve
准备阶段过后值为123,因为被final
修饰了
- 变量
解析
虚拟机将常量池内的符号引用替换为直接引用的过程
什么是符号引用?什么是直接引用
- 符号引用:以一组符号来描述所引用的目标
- 直接引用:直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄
初始化
执行类构造器
双亲委派模型
类加载器
从虚拟机角度,有两种加载器:
加载器 | 语言 | 代码 |
---|---|---|
启动类加载器 | c++ | BootstrapClassLoader |
其他类加载器 | java | java.lang.ClassLoader |
从java开发人员角度
加载器 | 路径 | 代码 |
---|---|---|
启动类加载器 | <JAVA_HOME>\lib |
BootstrapClassLoader |
扩展类加载器 | <JAVA_HOME>\lib\ext |
sun.misc.Launcher$ExtClassLoadr |
应用程序类加载器 | 用户类路径上所指定的类库 | ClassLoader |
类加载器之间的关系
image-20190501094604432双亲委派模型
定义
除顶层类加载器外,其余类加载器都应当有自己对应的父类加载器。双亲父母一辈的意思,并不是指两个
工作过程
如果一个类加载收到加载请求,它会首先让父加载器加载,父加载器加载失败,才会自己去加载
优点
可以通过类本身和其类加载器一同确定其在Java虚拟机中的唯一性
破坏双亲委派系统
什么场景下需要破坏
- 实现隔离性,如tomcat。tomcat服务器中可能部署多个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,要保证每个应用程序的类库是独立的,相互隔离。tomcat为了实现隔离性,每个
webappClassLoader
加载自己的目录下的class文件 -
SPI
串行外接接口,如JDBC
:父类加载器无法加载到所需的文件,Driver接口定义在jdk中,实现由服务商提供,不会放在<JAVA_HOME>/lib
下,只能由启动类加载器委托子类来加载
如何破坏
有三种方式:
- 重写
loadClass()
方法,loadClass
中包含了双亲委派的逻辑,通常自定义classLoader的时候会复写findClass
-
Thread
类中的setContextClassLoader
,作用是父类加载器请求子类加载器去完成加载的动作 -
OSGI
,动态模块系统。
网友评论