类加载<br>当程序主动使用某个类时,如果该类还未被加载到内存中,<br>则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。<br>如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化<br>
类的生命周期
加载<br><font color="#ffb74d">在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的入口</font><br>
class
jar
war
zip
连接
验证<br><font color="#ffb74d">确保 Class 文件的字节流中包含的信息是否符合当前虚拟机的要求<br>并且不会危害虚拟机自身的安全</font><br>
文件格式验证
元数据验证
字节码验证
符号引用验证
准备<br><font color="#ffb74d">为类变量分配内存,准备初始值<br>对final的静态字面值常量直接赋初值</font><br>
解析<br><font color="#ffb74d">将符号引用转换成直接引用</font>
符号引用<br><ol><li><font color="#ffb74d">符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟 机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引 用的字面量形式明确定义在 Java 虚拟机规范的 Class 文件格式中。</font></li><li><font color="#ffb74d">CONSTANT_Class_info</font></li><li><font color="#ffb74d">CONSTANT_Field_info</font></li><li><font color="#ffb74d">CONSTANT_Method_info</font></li></ol>
直接引用<br><font color="#ffb74d">直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有 了直接引用,那引用的目标必定已经在内存中存在。</font><br>
初始化<br><font color="#ffb74d">为静态变量赋值</font>
不初始化的场景(不初始化不代表不执行上面的加载和链接步骤)<br><ol><li><font color="#ffb74d">通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。</font></li><li><font color="#ffb74d">定义对象数组,不会触发该类的初始化。</font></li><li><font color="#ffb74d">常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触</font></li><li><font color="#ffb74d">发定义常量所在的类。</font></li><li><font color="#ffb74d">通过类名获取 Class 对象,不会触发类的初始化。</font></li><li><font color="#ffb74d">通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。</font></li><li><font color="#ffb74d">通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作。</font></li></ol>
使用
卸载
类加载的时机
主动引用<br><font color="#ffb74d">加载(loading)阶段,java虚拟机规范中没有进行约束,但初始化阶段,<br>java虚拟机严格规定了有且只有如下5种情况必须立即进行初始化(初始化前,必须经过加载、验证、准备阶段)<br></font><ol><li><font color="#ffb74d">使用new实例化对象时,读取和设置类的静态变量、静态非字面值常量(静态字面值常量除外)时,调用静态方法时。</font></li><li><font color="#ffb74d">对内进行反射调用时。</font></li><li><font color="#ffb74d">当初始化一个类时,如果父类没有进行初始化,需要先初始化父类。</font></li><li><font color="#ffb74d">启动程序所使用的main方法所在类</font></li><li><font color="#ffb74d">当使用1.7的动态语音支持时。</font></li></ol>
被动引用<br><ol><li><font color="#ffb74d">通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。</font></li><li><font color="#ffb74d">定义对象数组和集合,不会触发该类的初始化</font></li><li><font color="#ffb74d">类A引用类B的static final常量不会导致类B初始化(注意静态常量必须是字面值常量,否则还是会触发B的初始化)</font></li></ol>
类加载的方式
隐式加载<br><ol><li><font color="#ffb74d">创建类对象</font></li><li><font color="#ffb74d">使用类的静态域</font></li><li><font color="#ffb74d">创建子类对象</font></li><li><font color="#ffb74d">使用子类的静态域</font></li><li><font color="#ffb74d">在JVM启动时,BootStrapLoader会加载一些JVM自身运行所需的class</font></li><li><font color="#ffb74d">在JVM启动时,ExtClassLoader会加载指定目录下一些特殊的class</font></li><li><font color="#ffb74d">在JVM启动时,AppClassLoader会加载classpath路径下的class,以及main函数所在的类的class文件</font></li></ol>
显示加载<br><ol><li><font color="#ffb74d">ClassLoader.loadClass(className),只加载和连接、不会进行初始化</font></li><li><font color="#ffb74d">Class.forName(String name, boolean initialize,ClassLoader loader); 使用loader进行加载和连接,根据参数initialize决定是否初始化。</font></li></ol>