一、JVM的生命周期
虚拟机的声明周期可以分为三个阶段:
-
虚拟机的启动
虚拟机的启动是通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的,这个类是由虚拟机的具体实现来指定的。
-
虚拟机的执行
一个运行中的Java虚拟机有这一个清晰的任务就是执行Java程序,程序开始执行时虚拟机,当程序运行结束时,虚拟机就结束,在执行一个Java程序的时候,真真正正执行的是一个叫做Java虚拟机的进程。
-
虚拟机的退出
虚拟机的退出又分为几种情况:
- 程序正常执行结束
- 程序运行过程中遇到异常而终止
- 操作系统出现错误而导致Java虚拟机进程终止
- 某个线程调用Runtime类或System类的exit方法,或者Runtime类的halt方法,并且Java安全管理器也允许这次exit或者halt操作。
- 还有一种就是JNI(Java Native Interface)规范描述了用JNI Invocation API来加载或卸载Java虚拟机时,Java虚拟机退出的情况。
二、常见的Java虚拟机
- Classic VM:,它是世界上第一款商用虚拟机。这款虚拟机只提供解释器并没有即时编译器。
- Exact VM:sun提供的又一款虚拟机Exact Memory Management,准确式内存管理。
- HotSpot VM:大多数的jdk的默认虚拟机,也是Oracle JDK和Open JDK的默认虚拟机,其拥有解释器和即时编译器,在1.8的jdk中HotSpot中整合了JRockit虚拟机的优秀特性。
- JRockit:BEA的JRockit虚拟机,号称世界上最快的虚拟机,2008年BEA被Oracle收购。
- J9:IBM公司的J9虚拟机,是目前最右影响力的三大商用服务器之一。2017年,IBM发布了开源J9VM命名为OpenJ9,交给了Ecilpse基金会,也称为Ecilpase OpenJ9。
- TaobaoJVM:由AliJVM团队发布,是阿里巴巴基于OpenJDK开的自己的定制版本AlibabaJDk,它是国内第一个优化,深度定制且开源的高性能服务器。目前已经在淘宝天猫上线
三、虚拟机的类加载机制
3.1、概述
Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称为虚拟机的类加载机制,在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的。
3.2、类加载器的作用和角色
3.2.1 类加载器的作用
类加载器的作用就是将类class文件加载到内存中,对方法区中的类信息的访问都需要经过这个Class对象
3.2.1类加载器所扮演的角色
3.3、类加载过程
类的加载过程分为五个阶段
3.3.1、加载过程
类加载后会在堆区生成一个唯一的Class对象。
3.3.2、链接过程
在准备阶段如果变量是final static修饰的常量则不会赋予零值,而是会直接赋值如:以下代码会被直接赋值为123;
public static int value = 123
3.3.3、初始化过程
类构造器
注意:执行接口的类加载器
3.4、类加载的时机
类的加载过程只有五个阶段但是类的生命周期会经历,加载,验证,准备,解析,初始化,使用和卸载七个阶段,其中验证,准备,解析统称为链接。
对类的主动使用会触发类的初始化
java程序对类的主动引用:
添加一点:深入理解Java虚拟机第三版中提到当一个接口中定义了jdk8新加入的默认方法(被default关键字修饰的接口方法)时,如果这个接口的实现类发生了初始化,那改接口要在其之前被初始化
对类的别动引用不会触发类的初始化
java程序对类的被动引用:
- 通过子类引用父类的静态字段不会导致子类的初始化
- 通过数组定义来引用类,不会触发此类的初始化
- 常量在编译器会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化
四、类加载器的分类
- 启动类加载器
- 扩展类加载器
在jdk9中,扩展类加载器被平台类加载器所取代
- 应用程序类加载器
用户自定义的加载器:
为什么要自定义类加载器:
如何自定义类加载器:
自定义的类加载器需要继承ClassLoader,有关ClassLoader
如何获取类加载器:
通过方式一获取类加载器,如果获取到的结果为null,则表示是一个引导类加载器
例如:
// shareData是自定义类 ,String类是由引导类加载器加载的
public static void getClassLoader(){
String s = new String();
System.out.println("类加载器"+s.getClass().getClassLoader());
ShareData shareData = new ShareData();
System.out.println("类加载器"+shareData.getClass().getClassLoader());
}
注意:判断两个class对象是否是同一个类要注意两点:
- 类的全限定类型是否相等
- 加载类的类加载器是否相同
五、双亲委派模型
沙箱安全机制
双亲委派机制可以保护核心类不被篡改:
自定义一个java.lang.String类,在main方法中会报错
package java.lang;
/**
* @author mypc
* @create 2020 上午 11:40
*/
public class String {
//
static{
System.out.println("我是自定义的String类的静态代码块");
}
public static void main(String[] args) {
System.out.println("hello,String");
}
}
即使这个类是自定义的,理应由应用程序类加载器加载,但是由于双亲委派机制的存在,它会首先将该类交给它的父类加载器扩展类加载器加载,扩展类加载器又会交给它的父类加载器引导类加载器加载,而java.lang.String这个类在jdk的核心类库中存在,所以它会直接加载核心类库中的java.lang.String,不会加载我们自定义的java.lang.String,这样就防止了核心API被随意串改。java的核心类java.lang.String没有main方法,所以报错.
以上内容是根据尚硅谷的视屏和深入理解Java虚拟机第三版整理的笔记,如有错误请指出