提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、JVM简介
1.1 什么是虚拟机
虚拟机(Virtual Machine),就是一台虚拟的计算机。它是一款软件,用来执行一系列虚拟计算机操作,
一般的可以将将虚拟机分为 系用虚拟机 与 程序虚拟机
- Visual Box,VMware属于系统虚拟机,他们是对物理计算机的完全仿真,提供一个可运行完整操作系统的软件平台
- 程序虚拟机的典型代表就是Java虚拟机,它专门为执行单个计算机程序而设计,在Java虚拟机中执行的指令我们称为Java字节码指令
1.2 Java虚拟机
- Java虚拟机是一台执行Java字节码的虚拟计算机,它拥有独立的运行机制,其运行的Java字节码也未必由Java语言编译而成
- JVM平台的各种语言可以共享Java虚拟机带来的跨平台性,优秀的垃圾回收器,以及可靠的即时编译器
- Java技术的核心就是Java虚拟机(JVM,Java Virtual Machine),所有的Java程序都运行在Java虚拟机内部
1.3 JVM的位置
JVM是运行在操作系统之上的,它与硬件没有直接的交互
二、JVM整体结构
2.1 JVM整体结构图
2.2 Java代码的执行流程
Java程序 ==(通过Java编译器编译)==> 字节码文件 ==(Java解释器)==> 运行
2.3 JVM的架构模型
Java编译器输入的指令流基本上是一种基于 "栈的指令集架构",另一种指令集架构则是基于 "寄存器的指令集架构"
- 基于栈式架构的特点
设计与实现简单,不需要硬件支持,移植性好,跨平台,执行效率低于寄存器架构
指令流中的指令大部分是零地址指令,执行过程依赖于操作栈,指令集更小,指令多
- 基于寄存器架构的特点
指令集架构依赖硬件,移植性差,性能优秀,指令少
寄存器架构的指令集往往都是以一地址指令,二地址指令和三地址指令为主
三、JVM的生命周期
3.1 虚拟机的启动
Java虚拟机的启动是通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的
这个类是由虚拟机具体来实现的
3.2 虚拟机的执行
程序执行时运行,程序结束时就停止
执行一个Java程序的时候,真正执行的是一个叫做Java虚拟机的进程
3.3 虚拟机的退出
程序正常的退出
程序在执行过程中遇到了异常与错误而终止
操作系统错误导致停止
某线程调用Runtime类或者System类的exit方法,或者Runtime类的halt方法,并且Java安全管理器也允许这次操作
四、JVM发展历程
4.1 Sun Classic VM
在1996年Java1.0版本时,Sun公司发布了第一款Sun Classic VM虚拟机,是世界上第一款商用Java虚拟机
这款虚拟机只提供内部解释器
如果想使用JIT编译器,就必须进行外挂,但是一点使用JIT编译器解释器就不再工作,二者无法配合工作
现在Hotspot内置了此虚拟机
4.2 Exact VM
jdk1.2的时候sun公司提供了此虚拟机
Exact Memory Management:
-虚拟机可以知道内存中某个位置的数据是什么类型
具备高性能虚拟机的特点
-热点探测
-编译器与解释器混合工作
4.3 HotSpot
HotSpot最初由“Longview Technologies”公司设计,此后被sun公司收购,2009年sun公司又被甲骨文收购
JDK1.3的时候HotSpot VM称为默认虚拟机,且占据绝对市场地位
(未完成)
五、类加载器以及类的加载过程
5.1 JVM架构图
5.2 类加载器子系统的作用
- 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识
- ClassLoader只负责class文件的加载,至于是否可以运行则由Execution Engine决定
- 加载的类信息一块称为方法区的内存空间。除了类的信息外,方法区还会存放运行时常量池的信息,可能还包括字符串字面量和数字常量
5.3 类的加载过程
5.3.1 加载(Loading)
1.通过一个类的全限定名获取定义此类的二进制字节流
2.将这个字节流所代表静态存储结构转化为方法区的运行时数据结构
3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问接口
5.3.2 链接(Linking)
5.3.2.1 验证(Verify)
1.目的在于确保Class文件的字节流中包含信息符合当前虚拟机的要求,保证被加载类的正确性
2.主要包括四种验证:文件格式验证,元数据验证,字节码验证,符号引用验证
5.3.2.2 准备(Prepare)
1.为类变量分配内存并且设置该变量的默认初始值,即零值
2.这里不包含final修饰的static,因为final在编译的时候就会分配了,准备阶段会显示初始化
3.这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中
5.3.2.3 解析(Resolve)
1.将常量池内的符号引用转换为直接引用的过程
2.解析操作往往会伴随着JVM在执行完初始化之后再执行
3.符号引用:就是一组符号来描述所引用的目标,直接引用:就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄
4.解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。
5.3.3 初始化(initialize)
1.初始化阶段就是执行类构造器方法<clinit>()过程
2.此方法不需要定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来的
3.构造器方法中指令按语句在源文件中出现的顺序执行
4.<clinit>()不同于类的构造器
5.若该类具有父类,JVM会保证子类的<clinit>()执行前,父类的<clinit>()已经执行完毕
6.虚拟机必须保证一个类的<clinit>()方法在多线程下被同步加锁
六、类加载器的分类
1.JVM支持两种类型的类加载器,分别为 引导类加载器(Bootstrap ClassLoader) 和 自定义类加载器(User-Defined ClassLoader)
2.从概念上来说,自定义加载器一般指的是程序中开发人员自定义的一类加载器,但是Java虚拟机规范却没有这么定义,而是将”所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器“
3.无论如何划分,常见的类加载器一般只有三种:如下图所示
- 这里四者的关系是包含关系,不是上下层,也不是子父类的继承关系
6.1 启动类加载器(引导类加载器 Bootstrap ClassLoader)
- 使用C/C++语言实现的,嵌套在JVM内部
- 它用来加载Java的核心类库,用于提供JVM自身需要的类
- 并不继承自java.lang.ClassLoader,没有父类加载器
- 加载扩展类和应用程序类加载器,并指定他们的符类加载器
- 出于安全考虑,Bootstrap启动类加载器只加载报名为java、javax、sun等开头的类
6.2 扩展类加载器(Extension ClassLoader)
- 由Java语言编写
- 派生于ClassLoader类
- 父类加载器为启动类加载器
- 从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载
6.3 应用程序类加载器 (AppClassLoader)
- Java语言编写
- 派生于ClassLoader类
- 父类加载器为扩展类加载器
- 它负责加载环境变量classpath或系统属性,java.class.path指定路径下的类库
- 该类加载是程序中默认的类加载器,一般来说Java应用的类都是由它来完成加载
- 通过**ClassLoader.getSystemClassLoader()**方法可以获取到该加载器
6.3.1 什么情况下使用用户自定义类加载器
1.隔离加载类
2.修改类的加载方式
3.扩展加载源
4.防止代码泄露
6.3.2 用户自定义类加载器实现步骤
1.可以通过集成抽象类java.lang.ClassLoader类的方式实现自己的类加载器
2.JDK1.2之前,在自定义类加载器总会去集成ClassLoader并重写loadClass()方法,在JDK1.2之后则是建议把自定义类加载逻辑卸载findClass()方法中
3.在编写自定义类加载器时,如果没有过于复杂的要求,可以直接集成URLClassLoader类,这样可以避免自己去编写findClass()方法机器获取字节码的方式,使其更加简洁
6.3.3 关于ClassLoader的概念
ClassLoader类是属于一个抽象类,其后所有的类加载器都继承自ClassLoader(不包括引导类加载器)
- sun.misc.Launcher它是一个Java虚拟机的入口程序
七、双亲委派机制
7.1简述双亲委派机制
Java虚拟机对class文件采用的是“按需加载”的方式,也就是说在需要使用该类时才会将他的class文件加载到内存中生成class对象
而且加载某个类的class文件时,Java虚拟机采用的时“双亲委派模式”,即把请求交由父类处理,它是一种任务委派模式
7.2 工作原理
1.如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给符类的加载器去执行
2.如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终到达顶层的引导类加载器
3.如果父类加载器可以完成加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载
7.3 双亲委派机制的优势
1.防止类的重复加载
2.保护程序安全,防止API被随意篡改
7.4 沙箱安全机制
当我们自定义String类时,加载自定义的String类的时候会先使用引导类加载器加载,而引导类加载器在加载的过程中会先加载jdk自带的文件(rt.jar包中java/lang/String.class)
会报错没有main方法,就是因为rt.jar包中的String类。这样可以保证Java核心源代码的保护,这就是沙箱安全机制