1.Java和JVM的区别
Java是跨平台的语言,而JVM是跨语言的平台
- JVM只关心字节码文件。只要其他编程语言(未必是Java语言)的编译结果满足并包含JVM的内部指令集等信息,它就是有效的字节码文件,能够被JVM识别且装载运行:
- 不同的编译器可以编译出相同的字节码文件,字节码文件也可以在不同的JVM上运行:
- JVM和Java语言没有必然的联系,JVM只和Class文件格式所关联,Class文件中包含JVM指令集(或者称为字节码)和符号表以及其他辅助信息
2.虚拟机和JVM
虚拟机可以分为系统虚拟机和程序虚拟机:
- 系统虚拟机:完全是对物理计算机的仿真,如VMWare、Visual Box,提供了一个可以运行完整操作系统的软件平台
- 程序虚拟机:专门为执行单个计算机程序而执行,如在JVM中执行的指令称为Java字节码指令
JVM是一台执行二进制字节码的虚拟计算机。它拥有独立的运行机制,其运行的字节码也未必由Java语言编译而成。JVM平台的各种语言可以共享JVM带来的跨平台性、优秀的垃圾回器以及可靠的即时编译器
作用:JVM负责装载字节码到其内部,解释/编译为对应平台上的机器指令执行。每一条指令,JVM规范中都有详细定义,如怎么取操作数,怎么处理操作数,处理结果放在哪里等
特点:
一次编译,到处运行
自动内存管理
自动垃圾回收功能
3.JVM的位置
- JVM是运行在操作系统之上的,它与硬件没有直接的交互:
4.JVM的整体结构
- 下面结构图针对HotSpot VM,它采用解释器与即时编译器并存的架构:
5.Java代码执行流程
- 在下图中Java编译器为前端编译器,而JVM中的JIT编译器为后端编译器(操作系统只识别机器指令,不识别字节码):
6.JVM的架构模型
Java编译器输入的指令流一般基于下面两种架构。由于跨平台性的设计,Java的指令都是根据栈来设计的(不同平台CPU架构不同,所以不能设计为基于寄存器的):
基于栈的指令集架构(如HotSpot VM):
设计和实现更简单,适用于资源受限的系统
避开了寄存器的分配难题:使用零地址指令方式分配
指令流中的指令大部分是零地址指令,其执行过程依赖于操作栈。指令集更小,编译器容易实现
不需要硬件支持,可移植性更好,更好实现跨平台
性能下降,实现同样的功能需要更多的指令
# 执行2+3的操作 iconst_2 //常量2入栈 istore_1 iconst_3 // 常量3入栈 istore_2 iload_1 iload_2 iadd //常量2/3出栈,执行相加 istore_0 // 结果5入栈
基于寄存器的指令集架构:
典型的应用是x86的二进制指令集:比如传统的PC以及Android的Davlik虚拟机
指令集架构则完全依赖硬件,可移植性差
性能优秀和执行更高效
花费更少的指令去完成一项操作
在大部分情况下,基于寄存器架构的指令集往往都以一地址指令、二地址指令和三地址指令为主,而基于栈式架构的指令集却是以零地址指令为主
# 执行2+3的操作 mov eax,2 //将eax寄存器的值设为2 add eax,3 //使eax寄存器的值加3
7.JVM的生命周期
虚拟机的启动:JVM的启动是通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的,这个类是由虚拟机的具体实现指定的
虚拟机的执行:
运行中的JVM的任务为执行Java程序
程序开始执行时开始运行,程序结束时就停止
执行Java程序时真正在执行的是JVM的进程
虚拟机的退出:
程序正常执行结束
程序在执行过程中遇到了异常或错误而异常终止
由于操作系统用现错误而导致Java虚拟机进程终止
某线程调用Runtime类或system类的exit方法,或Runtime类的halt方法,并且Java安全管理器也允许这次exit或halt操作
JNI(Java Native Interface)规范描述了用JNI Invocation API来加载或卸载JVM时
8.JVM的发展历程
Sun Classic VM:世界上第一款商用JVM,这款虚拟机内部只提供解释器,如果使用即时编译器,就需要进行外挂
解释器进行逐行解释字节码,效率比较低
即时编译器会把热点代码缓存起来,那么以后使用热点代码的时候,效率就比较高
解释器和编译器不能配合工作
Exact VM:虚拟机可以知道内存中某个位置的数据具体是什么类型
HotSpot VM:名称中的HotSpot指的就是它的热点代码探测技术
- 通过计数器找到最具编译价值代码,触发即时编译或栈上替换
通过编译器与解释器协同工作,在最优化的程序响应时间(解释器负责)与最佳执行性能(编译器负责)中取得平衡
JRockit:专注于服务器端应用。不太关注程序启动速度,因此内部不包含解析器,全部代码都靠即时编译器编译后执行
…
tips:
- 字节码不是先给解释器再给即时编译器执行,只使用解释器/即时编译器也是可以完成完成编译的
- 为什么不选择将所有字节码全部使用即时编译器进行缓存,而只是缓存热点代码?如果全部进行缓存会导致程序启动时响应时间过长