我们都知道JVM虚拟机的可执行文件为.class文件,那么什么时候JVM虚拟机会加载自己所需要的类呢?之前自己一直有这样的问题,上网找过好多网友的解释,感觉好像理解但是自己却无法说清楚,今天看了《深入理解JVM虚拟机》一书的讲解感觉自己有些透了,在此记录下来自己的理解~!
类的生命周期
类加载过程(主动|被动)
类的主动引用(一定会发生类的初始化)
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则先初始化他的父类
- 当要执行某个程序时,一定先启动main方法所在的类类的被动引用(不会发生类的初始化)
- 当访问一个静态变量时,只有真正生命这个静态变量的类才会被初始化(通过子类引用父类的静态变量,不会导致子类初始化)
- 通过数组定义类应用,不会触发此类的初始化 A[] a = new A[10];
- 引用常量(final类型)不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)
类加载的阶段之加载
- 通过一个类的权限定名来获取定义此类的二进制字节流
- 将这些二进制流所代表的静态存储结构转化为方法去的运行时数据结构
- 在堆中生成一个代表这个类的java.lang.Class对象,作为方法去这个类的各种数据的访问入口
类加载的阶段之验证
是否符合JVM规范,没有安全问题。大致分为四个阶段:文件格式验证、元数据验证、字节码验证、符号引用验证
类加载的阶段之准备
为类变量(仅包括被static修饰的变量)分配内容并设置类变量初始值的阶段,这些变量所使用的内存都将在方法去中进行分配。
类加载的阶段之解析
解析阶段是虚拟机将常量池的符号引用替换为直接引用的过程。(A.a = “Hello”替换为A.a指向“Hello”的地址)
类加载的阶段之初始化
初始化阶段时执行类构造器()方法的过程,()方法是由变一起自动手机类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的。
- 执行顺序为语句在源文件中出现的顺序(静态语句块只能访问定义在其之前的变量;定义在它之后的变量可以复制,但不能访问)
- 执行当前类的()方法时,其父类的方法已经执行完毕
父类的方法先执行,也就意味着父类中定义的静态语句块要优于子类的变量赋值操作
- 虚拟机会保证一个类的构造器方法在多线程环境中被正确加锁和同步
- 接口与类的初始化不同的是,执行接口的方法不需要先执行父类的方法,只有当父类接口中定义的变量使用时,父类接口才会初始化
类加载的阶段之类的使用过程
//详情请见代码
类加载的阶段只类的卸载过程
在Java虚拟机中类的生命周期和对象的生命周期很相似。虚拟机创建并初始化对象,使程序使用对象,然后在对象变得不再被引用后可选地进行垃圾收集。同样,虚拟机装载、连接并且初始化类,使程序能使用类,当程序不再引用他们的时候可选的卸载它们。如果程序不再引用某类型,那么这个类型就变成不可触及,所以可以被垃圾收集(俗称GC)。但需要注意的是JVM自带的类加载器(PS:Bootstrap、ExtClassLoader、AppClassLoader)装载的类型永远是可触及的,所以永远不会被卸载。只有使用用户定义的类装载器装载的类型才会变成不可触及的,从而被虚拟机回收。