基础概念
1. 常用操作
1.1. 查看字节码(反编译)
javap -v xxx.class
2. 虚拟机分类
系统虚拟机:模拟硬件环境,是对物理计算机的仿真。比如VirtualBox,VMware
程序虚拟机:为执行单个程序而设计。比如JVM
3. 解释器与即时编译器
解释器直接运行,响应时间短,但是执行速度慢
即时编译器需要一段时间准备后才能运行,响应长,但是执行速度快
JVM混合使用二者
4. JVM历史(精简)
4.1. Sun Classic VM
- 世界上第一款商用JVM,由Sun公司在1996年和Java1.0一同发布,JDK1.4时被淘汰
- JVM内部只提供解释器
- 只能通过外挂方式使用JIT,而且解释器不能和JIT协同工作(二者只能用一个)
4.2. Exact VM
- Sun在Java1.2时短暂使用过
- 准确式内存管理,JVM可以知道内存某处的数据具体是什么类型(值还是地址)
- 可以热点探测;可以让编译器和解释器混合工作
4.3. HotSpot VM
- JDK1.3开始成为Java默认虚拟机
- 市场定位是多用途的
4.4. JRockit
- 专注于服务器端
- 不关注启动速度,所以不包含解释器,全靠即时编译,所以JRockit是世界上最快的JVM
4.5. J9
全称IBM Technology for JVM,简称IT4J,内部代号是J9。
市场定位也是多用途
4.6. TaobaoJVM
基于OpenJDK HotSpot VM,深度定制并开源
- GCIH,off-heap
4.7. Davik VM
- Android的虚拟机
- 它没有遵循JVM规范:1. 指令架构采用寄存器架构 2. 字节码不是
.class
文件不是.dex
- Android5.0后被Art VM(支持提前编译,AOT)替换
5. 三大主流JVM
JVM的3种主流实现:
HotSpot(JDK默认JVM),Sun(已被Oracle收购)开发的
JRockit,BEA(已被Oracle收购)开发的
J9,IBM开发的(已开源)
6. JVM指令架构模型
JVM接收的指令流(字节码内容)有两种,一种是基于栈的指令集架构,一种是基于寄存器的指令集架构
典型例子 | 指令集特点 | 跨平台 | |
---|---|---|---|
基于栈的指令集 | HotSpot用的指令集 | 1. 大部分是0地址指令(避开了寄存器的分配难题,实现简单) 2. 指令集更小;完成操作所用的指令更多(1个2+3的操作,需要9条指令);性能更差 |
不依赖硬件,可以跨平台 |
基于寄存器的指令集 | PC的x86,Android的Davlik | 1.大部分多地址指令 2.指令集更大;完成一个操作,所用的指令更少(同样的2+3只需要2条指令);性能更高 |
依赖硬件,跨平台差 |
7. JVM的生命周期
7.1. 启动
引导类加载器(Bootstrap Class Loader)加载一个初始类(initial class)完成JVM的启动。至于初始类是什么,这是由虚拟机的具体实现决定的
7.2. 执行
Java程序开始运行时JVM才运行,Java程序结束时JVM就停止。执行一个Java程序实际上是在执行一个叫做JVM的进程
打印当前运行的Java进程
jps
7.3. 退出
有以下几种情况:
- 程序正常结束
- 程序因为异常或错误结束
- OS出错导致JVM进程结束
- 程序调用
System.exit()
,Runtime.exit()
,Runtime.halt()方法
结束自己 - JNI中的API
JVM概览
1. 类加载器子系统
- 加载 Loading 把字节码文件数据读入内存
- 链接 Linking
- 初始化 Initialization 给静态变量赋初值
2. 运行时数据区
JVM管理内存的结构
- 方法区 Method Area 类的信息,比如静态变量,常量
- 堆区 Heap Area 所有对象实体
- 每个线程一份的
- 程序计数器 PC Registers
- JVM栈区 Stack Area
- 本地方法栈 Native Method Stack
3. 执行引擎
- 解释器:逐行把字节码翻译成机器指令
- JIT:把热点代码的字节码翻译成机器指令存起来
- 垃圾回收器
类加载器
负责从文件系统或者网络中加载.class
文件到内存中, 至于它能否运行由执行引擎决定
1. 加载
- 通过类的全限定名获取此类的二进制字节流
- 加载
.class
文件有很多途径
- 加载
- 把字节流所代表的静态存储结构转化成方法区的运行时数据结构
- 在内存中生成一个代表这个类的
java.lang.Class
对象,作为方法区中这个类的各种数据的访问入口
2. 链接
2.1. 验证
检测字节流中的信息是否符合当前JVM的要求(CA FE BA BE开头),是否会危害JVM自身的安全
2.2. 准备
为类变量(static)分配内存,并且设置类变量的初始值,即“零值”
常量(static final)在编译时已经分配内存和赋零值了,它在准备阶段进行真的初始化
2.3. 解析
把符号引用转换位直接引用。 下图中所圈的就时符号引用,在解析时会把它们换成对应字节码文件的地址