java类的生命周期
- 类加载过程
- 加载 :
- 通过类全名获取二进制字节流,不一定是读取class文件,也可能是jar,war或者通过网络等。
- 将class文件转化为方法区运行时数据结构
- 生成java.lang.Class对象,为方法区的类信息提供访问入口
- 连接
- 验证:文件格式验证,元数据验证,字节码验证,符号引用验证
- 准备:为静态变量分配内存并设初值,这个初值是0,false等,而不是代码中静态变量赋值语句。JDK 8及之后, 类变量则会随着Class对象一起存放在Java堆中
- 解析:将常量池中的符号引用替换为直接引用
- 直接引用(Direct References) : 直接引用是可以定位到内存地址的标记,可以是直接指向目标的指针、 相对偏移量或者是一个能间接定位到目标的句柄。
- 加载 :
- 初始化:执行< clinit >()方法和< init >()方法
- < clinit >()方法:前端编译器将源文件中的静态变量赋值语句,静态代码块按顺序自动生成< clinit >()方法
- < clinit >()方法:类构造器
- < init >()方法:实例构造器,也是前端编译器自动生成的
- JVM保证父类的< clinit >()方法一定在子类< clinit >()方法前执行
- 类的初始化时机比如
- 使用某个类
- 调用某个类的静态方法
- main方法是程序的入口,也是静态方法
- 使用某个类的类的类变量
- 调用某个类的静态方法
- 创造某个类的实例
- new
- 反射
- 初始化某个类时必须先初始化其父类
- 使用某个类
- < clinit >()方法:前端编译器将源文件中的静态变量赋值语句,静态代码块按顺序自动生成< clinit >()方法
- 使用
- 卸载
类加载器
类的命名空间,包括类加载器和这个类本身。如果一个class文件用不同的类加载器加载,加载后的类就是不等的。
双亲委派模型
类加载器的分类
- 启动类加载器(Bootstrap Class Loader) : 加载<JAVA_HOME>\lib目录下的类。在虚拟机内,使用C++实现
- 扩展类加载器(Extension Class Loader) : 它负责加载<JAVA_HOME>\lib\ext目录中类库。JDK允许用户将具有通用性的类库放置在ext目录里以扩展Java SE的功能
- 应用程序类加载器(Application Class Loader) : 由于应用程序类加载器是ClassLoader类中的getSystemClassLoader()方法的返回值, 所以有些场合中也称它为“系统类加载器”。 它负责加载用户类路径(ClassPath) 上所有的类库。
- 用户可以自定义的类加载器来实现加载除了磁盘位置之外的Class文件,加载加密后的class文件, 实现类的隔离、 重载等功能。
- 启动类之间的关系是组合而不是继承。
- JDK9的类加载的层次有变动,待补充…
双亲委派的过程
即父类加载器优先加载类。一个类加载器收到了类加载的请求,会首先看是否加载过了,如果未加载过,它首先会委托父类加载,层层委托直到启动类加载器。只有父类加载器无法加载这个类,子类才会加载。
特点
- 双亲委派模型并不是一个具有强制性约束的模型,自己实现的类的加载器可以破坏这个规则。
- 这个模型中,越基础的类会被越上层的类加载器加载。但是如果一个基础类需要回调用户代码,就有问题了。SPI类的JNDI,JDBC等等都是这类的情况。线程上下文类加载器Thread Context ClassLoader加载类,这样就是父类加载器委托子类加载器加载了。JDK6提供了ServiceLoader和META-INF/services中的配置信息解决了SPI加载问题。
- 代码热替换(Hot Swap)、 模块热部署(Hot Deployment) 中的OSGi的类加载器不是树状而是网状的结构了。
- java9的模块化不提供支持多版本模块并存、 支持运行时热替换、 热部署模块的能力, 如果需要则集成OSGi。