目录
Parents Delegation Model 双亲委派机制
JVM是什么?
JVM(Java Virtual Machine)是Java 虚拟机的简称,它是一个可以执行 JAVA 字节码的虚拟 计算机。
掌握JVM后,能做什么?
- 防止内存泄漏(Memory Leak)
- 科学使用垃圾回收器(GC)
- 优化线程锁(Thread Lock)的使用
- 提高系统性能,降低系统延迟(Delay)
- 提高系统吞吐量
相关产品
- TaoBaoJVM (国内阿里团队研发)
- J9 (IBM团队 内网使用)
- HotSpot (Sun公司研发)
JVM结构
JVM由四大部分组成
- ClassLoader System 类加载系统: 负责加载类到运行内存中
- Runtime Data Area 运行时数据区: 负责存储对象、数据和方法
- Execution Engine 执行引擎:编译执行字节码文件,执行GC
- Native Interface 本地接口: 运行别的编程语言 为Java 所用
ClassLoader System 类加载系统
类加载系统负责加载类,验证类,解析类,初始化类对象,并转换为字节码文件。
- Bootstrap ClassLoader 基础类加载器,用C++编写,加载核心库,不能被Java调用,常见的String、Object基础类和包装类,由这个加载器完成,此外可加载这些包
JAVA_HOME/jre/lib/rt.jar
、resources.jar
、sun.boot.class.path
- ExtClassLoader 扩展类加载器,负责加载
JAVA_HOME\lib\ext
目录中的,或通过java.ext.dirs
系统变量指定路径中的类库 - AppClassLoader 应用类加载器,常用,自己写的类、Spring、Mybatis 等
- 自定义类加载器,应用于同包名类的加载,或者解析加密的class文件
Parents Delegation Model 双亲委派机制
双亲委派模型可以简单理解为向上询问、向下委托。
值得注意的是,图中的parent child关系并非继承关系。
以加载User自定义类为例,类加载器从AppClassLoader开始向上询问,其parent是否加载User,若ExtClassloader未加载,则继续向上询问BootstrapClassLoader是否加载了User,当BootstrapClassLoader发现自己未加载,则从BootstrapClassLoader开始尝试是否能加载该类,若未能加载,进一步传递给ExtClassloader加载,若未能加载,最终传递给AppClassLoader加载。
优点:
- 保证同一个包路径下的同一个包名类 有且仅会加载一次,保证了系统的健壮性(robustness)
缺点:
- 运行效率降低。手写的类,往往是由AppClassloader加载,然而重新向上询问,向下委托,往往是多余的。
- 若一定要加载同包名类,在双亲委派模型中,是不能被完成的,只有引入手写的自定义类加载器。
类的加载方式
- 显式加载,直接调用类加载器,例如启动mysql的驱动:
Class.forname("com.mysql.cj.jdbc.Driver");
- 隐式加载,通过new对象,或者调用静态方法时,加载类对象。
类的加载过程
加载阶段
.class文件的路径 决定了类加载器的不同,再交由各自类加载器完成类的加载,文件通过IO流进入JVM中。
连接阶段
- 校验,校验类结构信息的合法性
- 准备,对类变量进行默认初始化,为类变量设置分配内存
- 解析,虚拟机将常量值中的符号引用替换为直接引用,简单的来说,如果对象A引用了对象B的一个方法b(),单单一句b()是不够的,虚拟机进一步帮助A类,找到B类中b()的实际位置。
初始化阶段
- 主动使用
- 被动使用,被动使用的类不会加载静态方法
准备代码:
class A {
static {
System.out.println("A.static initializer");
}
public static void methodA() {
System.out.println("A.methodA");
}
}
class B extends A {
static {
System.out.println("B.static initializer");
}
public static void methodB() {
System.out.println("B.methodB");
}
}
执行:
A.methodA();
可以得到:
A.static initializer
A.methodA
此时, A类为 主动加载,并且完成了A类的初始化
调整main函数中的代码:
// A.methodA();
B.methodA();
可以得到:
A.static initializer
A.methodA
此时,B类中的初始化并没有执行。这时 B类属于被动加载,完成初始化的A类属于主动加载