JVM类加载机制
类加载时机
- 加载
- 验证
- 准备
- 解析
- 初始化
- 使用
- 卸载
加载
- 通过全类名来获取定义此类的二进制流(全限定名,用/替换.)
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据。
- 在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的访问入口。
加载二进制流的手段:
- 从zip包中获取,如jar
- 从网络中获取,Applet
- 运行时计算生成,动态代理
- 由其他文件生成,jsp
- 从数据库中读取,较少
注意:
数组类由不通过类加载器创建,而由虚拟机直接创建。
- 如果数组的组件类型(每一个值)是引用类型,则递归采用类的家在过程去加载
- 如果不是引用类型,而是int[]中,将数组标记为与引导类加载器关联
- 如果不是引用类型,则数组的可见类型默认为public
验证
- 文件格式验证,验证字节流是否符合Class文件格式的规范,并且是否能被虚拟机处理(版本号是否在处理范围)。
- 元数据验证 进行语义分析。
- 字节码验证 确定程序语义是否合法、符合逻辑。
- 符号引用验证 发生在虚拟机将符号引用转化为直接引用的时候。
准备
为类变量分配内存并设置类变量的初始值,这些变量所使用的内存都在方法区进行分配。这里只包括类变量,不包括实例变量。
- 内存分配的仅包括类变量(static),而不包括实例变量。
- 这里所设置的初始值"通常情况"下是数据类型默认的零值(如0、0L、null、false等)。
解析
将常量池内的符号引用替换为直接引用的过程,也就是得到类或者字段、方法在内存中的指针或者偏移量。
初始化
初始化一个类变量和其他资源,也就是执行类构造器()方法的过程
- 当遇到 new 、 getstatic、putstatic或invokestatic 这4条直接码指令时,比如 new 一个类,读取一个静态字段(未被 final 修饰)、或调用一个类的静态方法时。
- 当jvm执行new指令时会初始化类。即当程序创建一个类的实例对象。
- 当jvm执行getstatic指令时会初始化类。即程序访问类的静态变量(不是静态常量,常量会被加载到运行时常量池)。
- 当jvm执行putstatic指令时会初始化类。即程序给类的静态变量赋值。
- 当jvm执行invokestatic指令时会初始化类。即程序调用类的静态方法。
- 使用
java.lang.reflect
包的方法对类进行反射调用时如Class.forname("..."),newInstance()等等。 ,如果类没初始化,需要触发其初始化。 - 初始化一个类,如果其父类还未初始化,则先触发该父类的初始化。
- 当虚拟机启动时,用户需要定义一个要执行的主类 (包含 main 方法的那个类),虚拟机会先初始化这个类。
- MethodHandle和VarHandle可以看作是轻量级的反射调用机制,而要想使用这2个调用, 就必须先使用findStaticVarHandle来初始化要调用的类。
- 当一个接口中定义了JDK8新加入的默认方法(被default关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。
使用
卸载
卸载类的3个条件:
- 该类的所有的实例对象都已被GC,也就是说堆不存在该类的实例对象。
- 该类没有在其他任何地方被引用。
- 该类的类加载器的实例已被GC。
在JVM生命周期类,由jvm自带的类加载器加载的类是不会被卸载的。但是由我们自定义的类加载器加载的类是可能被卸载的。