文章目录
Java的跨平台性
- Java虚拟机不仅可以运行Java的字节码文件,也可以运行其他语言的字节码文件,比如JavaScript等,只要符合Java虚拟机的规范
JVM的特点:
- 一次编译,到处运行(字节码文件可以到处运行)
- 自动内存管理
- 自动的垃圾回收功能
不需要程序员只需要关注业务层面,降低了内存泄露和内存溢出的风险
JVM的位置
- JVM是运行在操作系统之上的,没有和硬件直接的交互
JVM的整体结构
Java程序的执行流程
详细图
JVM的架构模型
Java编译器输入的指令流指令集的架构模型主要分为两种,一种是基于栈的指令集架构
,一种指令集架构则是基于寄存器的指令集架构
而JVM默认使用的是HotPost虚拟机,而它的架构是基于栈的。
两者的区别
总结
- 由于跨平台的设计,Java的指令都是基于栈设计的。不同平台CPU架构不同,所以不能设计为寄存器架构的。
- 栈:跨平台性,指令集小,指令多;执行性能比寄存区差(寄存器架构和CPU的耦合较高,必须依赖于CPU,因此执行性能高,但是却不能跨平台)。
JVM的生命周期
虚拟机的启动
Java虚拟机的启动是通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的,这个类是由虚拟机具体指定的(不同的虚拟机有不同的初始类)。
虚拟机的执行
- 一个Java虚拟机都有一个清晰的任务:执行Java程序
- 程序开始执行时它才运行,程序结束时它就停止。
执行一个所谓的Java程序的时候,实际上执行的是一个叫做Java虚拟机的进程
虚拟机的退出
- 程序正常的运行结束
- 程序在执行过程中遇到了异常或错误而异常终止
- 由于操作系统错误而导致Java虚拟机进程终止
- 调用Runtime或者System的exit的方法
类加载过程
- 加载
- 链接
- 验证
- 准备
- 解析
- 初始化
类加载的分类
启动类加载器
引导类加载器,bootstrap ClassLoader
- 这个类是由C/C++语言实现的,嵌套在JVM的内部
- 它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar、resources.jar或sun.boot.class.path路径下的内容),用于提供JVM自身需要的类
- 并不继承自java。lang。ClassLoader,没有父加载器
- 处于安全考虑,Bootstrap启动类加载器只加载包围java、javax、sun开头的类
扩展类加载器
Extension ClassLoader
- 是用java语言编写的,由sun.misc.Launcher.ClassLoader实现
- 派生于ClassLoader
- 父类加载器为启动类加载器
- 从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用于创建的JAR放在次目录下,也会自动由扩展类加载器加载。
应用程序类加载器(系统类加载器,AppClassLoader)
- java语言编写,由sun.misc.Launcher$AppClassLoader实现
- 派生于ClassLoader
- 父类加载器为扩展类加载器
- 它负责加载环境变量classpath或系统属性 java.class.path指定下的类库
该类加载是程序中默认的类加载器
,一般来说,Java应用的类都是由它来完成加载- 通过ClassLoader$getSystemClassLoader()方法可以获取到该类的加载器
双亲委派机制
JVM对于类的加载采用的是双亲委派机制,即把任务交给父类处理,是一种任务委派。
工作原理:
- 如果一个类加载器收到了类加载请求,他并不会自己先去加载,而是把这个请求委托给父类的加载器去执行
- 如果父类加载器还存在其父类加载器,则进一步向上委托,一次递归,请求最终将达到顶层的启动类加载器
- 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。
这个思想和类加载过程(先加载父类再加载子类的思想)比较相似
为什么要使用双亲委派机制?
- 采用双亲委派机制可以防止内存中出现多份同样的字节码
- 类A和类B都需要加载System类,如果没有双亲委派机制,那么类A会加载一份,类B也会加载一份,那么在内存中就存在两份同样的字节码
- 采用双亲委派机制,会递归的向父类查找,也就是通过Bootstrap来加载,如果找不到再向下。这里的System类在bootstrap中加载,当类B也要加载System,也从bootstrap开始,此时bootstrap发现System类已经加载过了,那么就会直接返回内存中的System类即可,不需要重新加载,那么就只会有一份字节码
沙箱安全机制
出于安全的考虑。(防止核心API被随意篡改)假如有一个黑客,他重新了java.lang.String类,并重写了String类的某些方法(在方法中嵌入了部分病毒代码),如果没有双亲委派机制,那么该黑客重写之后的类就会被加载,并且在项目中发挥作用,但是如果有双亲委派机制,该重写的类是没有办法得到运行机会的,因此也能保证安全。
对于核心API的保护,就叫做沙箱安全机制
判断两个Class对象是同一个?怎么判断?
有两个条件,缺一不可
- 类的完成类名一致,包名一致
- 加载该类的ClassLoader(指ClassLoader实例对象)必须相同
其他
对于核心API的保护,就叫做沙箱安全机制
判断两个Class对象是同一个?怎么判断?
有两个条件,缺一不可
- 类的完成类名一致,包名一致
- 加载该类的ClassLoader(指ClassLoader实例对象)必须相同
其他
JVM必须知道一个类型是由启动类加载器加载的还是由用户类加载器加载的.。如果一个类型是由用户类加载器加载的,那么JVM会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中
。当解析一个类型到另一个类型的引用的时候,JVM需要保证这个两个类型的类加载器是相同的。