JVM
JVM是Java虚拟机,用来运行java程序,负责解释执行字节码文件
字节码可以由不同的编程语言通过编译获得(Kotlin,Jython……),只需要遵循JSR-292规范
JVM上可以用多语言混合编程(Kotlin……)
虚拟机
用软件模拟一台计算机,用来执行一系列虚拟的计算机指令。
虚拟机分为系统虚拟机与程序虚拟机
系统虚拟机:对物理计算机的仿真,提供一个可运行完整操作系统的软件平台
程序虚拟机:专门为执行单个计算机程序而设计,在Java虚拟机中执行的指令称为Java字节码指令
JVM的整体结构
Class文件
类装载器子系统
运行时数据区:方法区,堆,Java栈,本地方法栈,程序计数器
执行引擎
本地方法接口
本地方法库
JVM的架构模型
指令集架构有基于栈的指令集架构与基于寄存器的指令集架构
java属于基于栈的指令集架构
基于栈的指令集架构:
1、设计和实现更简单,适用于资源受限的系统
2、避开寄存器的分配难题
3、指令流中的指令大部分是零地址指令,依赖操作栈。指令集更小(指令多)
4、不需要硬件支持,可移植性更好,更好实现跨平台
基于寄存器架构:
1、指令集为x86指令集:传统pc与android的Davlik虚拟机
2、指令集架构则完全依赖硬件,可移植性差
3、性能优异,执行效率更高
4、一个操作花费的指令少
5、指令集往往都是一地址指令或二地址指令或三地址指令
JVM的生命周期
虚拟机的启动
java虚拟机的启动是通过引导类加载器创建一个初始类来完成,这个类是由虚拟机的具体实现所指定
虚拟机的执行
一个运行中的Java虚拟机有一个清晰的任务:执行Java程序(字节码)
程序开始执行时JVM才运行,程序结束时它就停止
执行一个所谓的Java程序的时候,在操作系统上真真正正的在执行的进程是Java虚拟机,有Java虚拟机解释执行字节码文件
虚拟机的退出
程序正常执行结束
程序在执行过程中出现异常或错误
由于操作系统的错误导致退出
某线程调用了Runtime类或者System类的exit方法或Runtime类的halt方法
使用JNI(Java Native Interface)的API来加载或者卸载Java虚拟机时,Java虚拟机会退出
硬件层面的关闭
类加载器子系统作用
负责从文件系统或者网络中加载class文件,class文件开头有特定的标识(CAFE BABE)
ClassLoader只负责class文件的加载,能否执行取决于Execution(执行引擎)
加载的类信息被放入方法区的内存空间中,除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量
类加载过程
加载——>链接(验证,准备,解析)——>初始化
对于没有装载的类,会先由ClassLoader装载
被装载的类进行链接
后进行初始化
调用类中的方法
……
结束
具体实现:
加载:
1、通过一个类的全限定类名获取定义此类的二进制字节流
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
链接:
验证:
目的在于保证Class文件的字节流符合虚拟机要求,保证加载类的正确性
四种验证:文件格式,元数据,字节码,符号引用
准备:
为类变量分配内存并且设置该类变量的默认初始值,即零值
准备阶段值的初始化不包括静态常量(final与static修饰),因为final在编译的时候就会分配,准备阶段会显示初始化
这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中
解析:
将常量池内的符号引用转换为直接引用的过程(将具体的值写入,不再是由符号代替)
解析在JVM执行完初始化后再执行
符号引用用来指代目标对象,但没有附上对象的地址,直接引用就直接指向对象的地址
解析动作主要针对类或接口,字段,类方法,接口方法,方法类型。
初始化:
初始化阶段就是执行类构造器方法()的过程
此方法不需定义,javac编译器自动收集类中的所有类变量的复制动作和静态代码块中的语句合并而来
构造器方法中指令按语句在源文件中出现的顺序执行
()不同于类的构造器,只是用来初始化类
虚拟机必须保证一个类的()方法在多线程下被同步加锁
注:,clinit只有在类中存在对静态变量赋值的情况下,才会有clinit