Java类的加载机制
1.类的加载,连接和初始化
1.1 JVM和类
- Java虚拟机是可运行JAVA代码的假想计算机.
- Java源文件通过编译器,能够产生相应的.class文件,也就是字节码文件,而字节码文件又通过Java虚拟机中的解释器(java虚拟机指令集)编译成特定机器上的机器码。
- Java源文件—->编译器—->字节码文件
- 字节码文件—->jvm—->机器码
当调用Java命令运行某个Java程序时,该命令将会启动一个Java虚拟机进程,不管该程序有多么复杂,该程序启动了多少个线程,他们都处于该Java虚拟机进程里。同一个JVM的所有线程,所有变量都处于同一个进程里,它们都使用该JVM的内存区,当系统出现以下几种情况时,JVM进程会被终止。
- 程序运行到最后正常结束
- 程序运行到使用System.exit()或Runtime.getRuntime().exit()代码结束程序。
- 程序执行过程中遇到未捕获的异常或错误而结束。
- 程序所在平台强制结束JVM进程。
public class A{
public static int a = 6;
}
public class Test1{
public static void mian(String[] args){
A a = new A();
a.a++;
System.out.println(a.a);
//输出结果为7
}
}
public class Test2{
public static void mian(String[] args){
A a = new A();
System.out.println(a.a);
//输出结果为6
}
}
两次运行Java程序处于两个不同的JVM进程中,两个JVM之间不会共享数据。
1.2 类的加载
当程序主动使用某一个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三个步骤来对该类进行初始化。类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象。类加载器通常由JVM提供,通过使用不同的类加载器,可以从不同来源加载类的二进制文件。
- 本地文件系统加载class文件
- JAR包加载class文件
- 通过网络加载class文件
- 把一个Java源文件动态编译,并执行加载
1.3 类的连接
当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中,类连接分为三个阶段。
- 验证:验证阶段用于检验被加载的类是否正确的内部结构,并和其他类协调一致。
- 准备:类准备阶段则负责为类的变量分配内存,并设置默认初始值。
- 解析:将类的二进制数据中的符号引用替换成直接引用。
1.4类的初始化
在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对类变量进行初始化,在java类中对类变量指定初始值有两种方式。
- 声明变量时指定初始值
- 使用静态初始化块为类变量指定初始值
1.5 类的初始化时机
- 创建类实例
- 调用某个类的静态方法
- 访问某个类的变量
- 初始化某个类有父类时,该类的所有父类都要初始化
- 直接使用java.exe命令来运行某个类
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
2 类加载器
2.1 类加载器简介
类加载器负责将.calss文件加载到内存中,并为之生成对应的java.lang.Class对象。一旦一个类被载入JVM中,同一个类就不会被再次载入了。一个类的全限定类名和其类加载器作为其唯一标识。当JVM启动时,会形成由三个类加载器组成的初始类加载器。
- Bootstrap ClassLoader:
根类加载器,它负责加载Java的核心类,他非常特殊,它并不是java.lang.ClassLoader的子类,而是JVM自身实现的。 - Extension ClassLoader:
扩展类加载器,它负责加载JRE的扩展目录(jre/lib/ext) - System ClassLoader:
系统类加载器,它负责加载来自java命令的-classpath选项,java.class.path系统属性,或者CLASSPATH环境变量所指定的JAR包和类路径。
2.2 类加载机制
JVM的类加载机制主要有如下三种
- 全盘负责:
所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显式使用另外一个类加载器来载入。
- 父类委托:
所谓父类委托,则是先让parent(父)类加载器视图加载该Class,只有在父类加器无法加载该类时才尝试从自己的路径中加载该类。
- 缓存机制:
缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓冲区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将起转成Class对象,存入缓冲区中。这就是为什么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。