类加载器 - 类的加载、连接与初始化
2019-11-22 10:04
Java代码中,类型的加载、连接与初始化过程都是在程序运行期间完成的
类型:可以理解为一个class
加载:查找并加载类的二进制数据,最常见的情况是将已经编译完成的类的class文件从磁盘加载到内存中
连接:确定类型与类型之间的关系,对于字节码的相关处理
验证:确保被加载的类的正确性
准备:为类的静态变量分配内存,并将其初始化为默认值。但是在到达初始化之前,类变量都没有初始化为真正的初始值
解析:在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用转换为直接引用的过程
初始化:为类的静态变量赋予正确的初始值
使用:比如创建对象,调用类的方法等
卸载:类从内存中销毁
理解:public static int number = 666;
上面这段代码,在类加载的连接阶段,为对象number分配内存,并初始化为0;然后再初始化阶段在赋予正确的初始值:666
Java程序对类的使用方式可分为两种
主动使用
创建类的实例
访问某个类或接口的静态变量,或者对静态变量赋值
调用类的静态方法
反射
初始化类的子类
Java虚拟机启动时被标明为启动类的类(包含main方法)
JDK1.7开始提供的动态语言支持(java.lang.invoke.MethodHandle实例的解析结果REF_getStatic,REF_putStatic,REF_invokeStatic句柄对应的类没有初始化,则初始化)
被动使用
除了主动使用的七种情况之外,其他使用Java类的方法都被看作是对类的被动使用,都不会导致类的初始化
所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”时才初始化他们
代码一
public class Test01 {
public static void main(String[] args) {
System.out.println(Child01.str);
}
}
class Father01 {
public static String str = "做一个好人!";
static {
System.out.println("Father01 static block");
}
}
class Child01 extends Father01 {
static {
System.out.println("Child01 static block");
}
}
运行结果做一个好人!
Father01 static block
做一个好人!
代码二
public class Test01 {
public static void main(String[] args) {
System.out.println(Child01.str2);
}
}
class Father01 {
public static String str = "做一个好人!";
static {
System.out.println("Father01 static block");
}
}
class Child01 extends Father01 {
public static String str2 = "做一个好人!";
static {
System.out.println("Child01 static block");
}
}
运行结果
Father01 static block
Child01 static block
做一个好人!
分析:
代码一中,我们通过子类调用父类中的str,这个str是在父类被定义的,对Father01主动使用,没有主动使用Child01,故Child01的静态代码块没有执行,父类的静态代码块被执行了。 -> 对于静态字段来说,只有直接定义了该字段的类才会被初始化。
代码二中,对Child01主动使用;根据主动使用的7种情况,调动类的子类时,其所有的父类都会被先初始化,所以Father01会被初始化。 -> 当一个类初始化时,要求其父类全部都已经初始化完毕了。
以上验证的是类的初始化情况,那么如何验证类的加载情况呢,可以通过在启动的时候配置虚拟机参数:-XX:+TraceClassLoading查看
运行代码一,查看输出结果,可以看见控制台打印了very多的日志,第一个加载的是java.lang.Object类(不管加载哪个类,他的父类一定是Object类),后面是加载的一系列jdk的类,他们都位于rt包下。往下查看,可以看见Loaded classloader.Child01,说明即使没有初始化Child01,但是程序依然加载了Child01类。
[Opened /usr/local/jdk1.8/jre/lib/rt.jar]
[Loaded java.lang.Object from /usr/local/jdk1.8/jre/lib/rt.jar]
...
[Loaded java.lang.Void from /usr/local/jdk1.8/jre/lib/rt.jar]
[Loaded classloader.Father01 from file:/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/]
[Loaded classloader.Child01 from file:/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/]
Father01 static block
做一个好人!
[Loaded java.lang.Shutdown from /usr/local/jdk1.8/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /usr/local/jdk1.8/jre/lib/rt.jar]
拓展:JVM参数介绍
因为前一章节使用了JVM参数,所以对其做一下简单的介绍
所有的JVM参数都是以-XX:开头的
如果形式是:-XX:+