JVM概述
JVM(Java Virtual Machine)主要任务就是负责将字节码装载到其内部,解释/编译为对应平台上的机器指令执行。
主要包含四大部分:类加载器、运行时数据区、本地方法接口、执行引擎。
程序在执行之前将Java代码转换为字节码(class文件),JVM通过类加载器将文件加载至运行时数据区,通过执行引擎将字节码翻译成底层系统指令再交由CPU执行,此过程需要调用本地库接口来实现程序功能。
类加载器(ClassLoader)
加载
通过类名(地址)获取此类的二进制字节流,在内存中生成一个代表这个类的java.lang.Class对象。
链接
- 验证:验证字节码文件格式是否符合当前虚拟机支持的语法格式。
- 准备:为静态属性分配内存,并赋默认初始值。
- 解析:将字节码中符号引用换为直接引用。
初始化
- 通过new对象,创建类的实例
- 访问某个类或接口的静态变量,或者对该静态变量赋值
- 调用类的静态方法
- 反射Class.forName(“”)
- 初始化一个类的子类(会首先初始化子类的父类)
类的初始化顺序
- 父类static—>子类static----->父类构造方法----->子类构造方法
类的加载器分类
- 引导类加载器(Bootstrap ClassLoader:启动类加载器)
这个类加载器使用C/C++语言实现嵌套在JVM内部,它只加载存放在<JAVA_HOME>\lib的java核心类库。(null)
- 扩展类加载器(Extension ClassLoader )
由java语言编写的,由sun.misc.Launcher$ExtClassLoader实现,从 JDK 系统安装目录的 jre/lib/ext 子目录(扩展目录)下加载类库,上层类加载器为引导类加载器。
- 应用程序类加载器(系统类加载器 Application ClassLoader)
由java语言编写,由sun.misc.Launcher$AppClassLoader实现;加载我们自己定义的类(class文件夹中),上层类加载器为扩展类加载器。
- 自定义类加载器(User-Defined ClassLoader)
tomcat中有加载器。
注:加载核心类时,通过是启动类加载器;自己创建类,通过应用程序类加载器。
双亲委派机制
-
如果一个类加载器收到类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行。
-
如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器。
-
如果父类加载器可以完成类的加载任务,就成功返回,倘若父类加载器无法完成加载任务,子加载器才会尝试自己去加载,这就是双亲委派机制。
-
如果均加载失败,就会抛出ClassNotFoundException异常。
优点
- 安全,可避免用户自己编写的类动态替换 Java 的核心类,如 java.lang.String
- 避免全限定命名的类重复加载(使用了 findLoadClass()判断当前类是否已加载)
主动使用和被动使用
主动使用:需要初始化类
- 通过new关键字导致类的初始化
- 访问类的静态变量,包括读取和更新
- 访问类的静态方法
- 对某个类进行反射操作,会导致类的初始化
- 初始化子类会导致父类的初始化
- 执行该类的main函数
被动使用:不用初始化类
-
引用该类的静态常量,且已经指定字面量的常量
-
构造某个类的数组时不会导致该类的初始化
注:主动使用和被动使用的区别在于类是否会被初始化