一、类加载器
- 类加载子系统(class Loader Subsystem)
- 内存空间(运行时数区域:Runtime data areas)
- 执行引擎(Execution Engine)
二、类加载
1、类加载的时机
①创建的一些实例:new对象时,会对类进行初始化,前提是这个类没有被初始化
②通过class文件反射创建对象
③调用类的静态属性,或静态属性赋值
④调用类的静态方法,也会进行类加载
⑤初始化一个类的子类时,使用子类的时候先初始化父类
⑥java虚拟机启动时被标记为启动类的类。比如:main方法所在类的类
2、类不会被加载的情况
①同一个java虚拟机中,一个类中只能被加载一次,如果已经被初始化的类不会加载
②在编译的时候能确定下来的静态遍历。不会对类进行初始化。
3、JVM中类加载器的执行(双亲委派机制)
Java提供了三个默认的类加载器:
BootStrap ClassLoader、Extension ClassLoader、Application ClassLoader。
双亲委派模型:
-
如果一个类加载器收到类加载的请求,不会自己去尝试加载这个类,而是将请求委派给父类去完成,依次向上
-
好处:
①防止内存中出现多类同样的字节码(不同类加载器指定的目录下才起作用)避免用户自己编写的类动态替换Java的一些核心类。
②单一性机制:一个类只能被加载一次,避免类的重复加载,因为JVM判定两个类是否是同一个类不仅仅根据类名,还需要判断加载该类的类加载器是否是同一个类加载器。相同的class文件被不同的类加载器加载到的结果,是不同的两个类。③Java类随着它的类加载器一起具备了一种带有优先级的层次关系。
例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的Bootstrap ClassLoader进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。
因此,如果开发者尝试编写一个与rt.jar类库中重名的Java类,可以正常编译,但是永远无法被加载运行
- BootStrap ClassLoader 加载$JAVA_HOME中jre/lib/rt.jar里所有的类
- Extension ClassLoader 加载JAVA平台上扩展功能的jar包。包含JAVA_HOME中jre/lib/ext/ *.jar或者 -DJava.ext.dirs指定目录下的jar包
- Application ClassLoader 加载classpath中指定的jar和目录中的class
- Costom ClassLoader 自定义的类加载器
4、类加载的过程
- 当appClassLoader加载一个class时,不会自己去尝试加载这个类, 而是将类加载请求委托给父类加载器Extension classLoader去完成
- 当Extension classLoader加载class时,不会自己去尝试加载这个类, 而是将类加载请求委托给父类加载器BootStrap ClassLoader去完成
- 如果BootStrap ClassLoader加载失败,会使用Extension classLoader尝试加载
- 如果Extension classLoader加载失败,会使用app classLoader尝试加载
- 如果app classLoader也加载失败,会抛出classNotFoundException
注:以上只要一个加载器加载成功的话,就会直接返回该类
5、加载类的方法
loadClass方法、findClass方法和defineClass方法
loadClass:负责以双亲委托方式去加载类
findClass:就根据类的包路径找到class文件
defineClass:负责从class字节码中加载Class对象,
然后Class对象通过反射机制才生成对象的
如果你自己实现类加载器,你的想法是什么样子的?
重写一个方法:findClass:(loadClass不能被重写,defineClass没必要重写,它是用C/C++写的)
6、类的生命周期
加载→ 验证→ 准备→ 解析→ 初始化→ 使用→ 卸载
链接阶段(验证→准备→解析)
加载Loading:查找并加载类的class文件(双亲委派机制)
验证Verification:文件的格式、元数据、字节码、符号引用
准备Preparation:为类中的静态变量分配空间,并将其初始化为默认值
解析Resolution:将类中的符号引用(常量池内的符号引用)转化为直接引用。(比如String s =“hello”,转化为 s=的地址指向“hello”的地址)
初始化Initialization:为类中的静态变量赋予正确的初始值