JVM概述
JVM就是二进制字节码的运行环境,负责装载字节码到其内存,解释/编译为对应平台上的机器指令执行,每条java指令在java虚拟机规范中都有详细定义,包括如何取、处理操作数等;
JVM特点如下
- 一次编译,到处运行(各CPU的架构不同的情况下JVM为了实现跨平台,字节码指令不能采用寄存器方式执行,目前都是按照栈的方式存取的;栈的特点就是指令多,指令集小,可跨平台)
- 自动内存管理
- 自动垃圾回收功能
JVM分类
目前实现JVM规范的主流的虚拟机包括Hotspot,JRocket,J9;
Hotspot
hotspot属于Oracle产品,目前占有绝对的市场地位
- 过去主流的jdk6以及目前主流的jdk8,以及sun/open jdk中的JVM默认都是hotspot;
- 方法区的概念只有在hotspot中存在,在J9以及JRocket不存在;
- 通过执行引擎中的解释器和JIT编译器(也称为后端编译器,java源代码编译为字节码文件中的编译器称为前端编译器,它们统一称为编译器)协同工作,在最优化的响应时间(主要体现在解释器)和最佳执行性能(主要通过JIT)取得平衡
JRocket
在商用领域,JRocket也是比较流行的虚拟机,它和Hotspot都属于Oracle产品,而且它尤其专注于服务器端应用领域
- JRocket号称是世界上最快的JVM;
- 追求的是程序最佳的执行性能,所以JRocket执行引擎中只存在JIT编译器,而没有解释器
- JDK8中,Oracle将JRocket当中有限的特性(毕竟两者架构完全不同)整合到Hotspot中
J9
属于IBM产品,在商用领域比较有影响力的JVM之一
- 在IBM相关产品中使用广泛且稳定,但在其他产品一言难尽
- 2017年,IBM发布了Open J9 产品并交由Eclipse基金会管理
扩展
sun jdk最初是sun公司非开源产品,而openjdk属于Oracle开源的产品;虽然Sun JDK和OpenJDK是Java开发工具包(JDK)的两种不同实现,但是从功能和性能上来说,Sun JDK和OpenJDK现在基本认为是相同的;而hotspot虚拟机是openjdk默认的虚拟机
JVM生命周期
虚拟机的启动
Java虚拟机的启动是通过引导类加载器bootstrap classloader 创建一个初始类来完成的,这个类是由虚拟机的具体实现(如Hotspot,J9等)指定的
虚拟机的执行
一个运行的java虚拟机有一个虚拟机进程专门执行java程序,程序开始执行它才运行,程序结束时它就停止
虚拟机的退出
程序正常执行结束,比如我正常关闭了应用服务器
程序在执行过程中遇到异常或者错误而终止
操作系统出现错误而导致JVM虚拟机终止
程序调用了runtime或者system的exit方法,强制退出JVM
JVM的位置
JVM体系结构(核心!!!)
JVM主要由三大部分组成,分别是类加载子系统,运行时数据区,执行引擎
类加载子系统
类加载器通过执行加载、链接(验证->准备->解析)、初始化后将字节码文件(java源代码使用编译器(javac)编译为字节码文件)加载到内存(即运行时数据区的方法区中);
运行时数据区(即JVM内存!!!)
包括堆,方法区,虚拟机栈,本地方法栈,程序计数器,JVM会将不同类型的对象分配到堆,栈,方法区中,其中:
- 堆用于存储对象实例。在运行时,当需要创建对象时,JVM将在堆中分配内存给新对象
- 栈用于存储局部变量和方法调用。每个线程在运行时都会有一个对应的栈帧,用于存储 方法参数、局部变量以及方法返回值等信息
- 方法区用于存储已加载类的结构信息,包括类的字段、方法代码以及常量池等
- 程序计数器则记录当前线程执行的字节码指令的地址
扩展
- 方法区和堆是被所有线程共享的区域,虚拟机栈和程序计数器以及本地方法栈都是线程私有的
- 每个线程都有独立的虚拟机栈,本地方法栈,程序计数器;
- 只有方法区和堆才会产生GC以及OOM异常,虚拟机栈和本地方法栈仅会出现OOM,程序计数器不会出现GC以及OOM;
执行引擎
作用是将加载到内存中方法区的字节码指令解释为机器指令,然后申请CPU资源解释执行;主要由解释器,JIT即时编译器,GC组成,其中:
- 解释器(Interpreter): 按照字节码指令逐条解释执行,可以快速启动,但执行速度相对较慢。
- 即时编译器(Just-in-Time Compiler,JIT): 将热点代码(被频繁执行的代码)编译为本地机器 码,以提高执行速度。JIT会对统计信息进行分析,并对经常执行的代码进行动态优化。
- 垃圾回收(Garbage Collection): JVM通过垃圾回收机制自动管理内存,并释放不再使用的对象内存空间,垃圾回收过程主要包括以下步骤:
- a. 标记(Marking): 标记存活的对象
- b. 清除(Sweeping): 清除或回收未标记的对象
- c. 压缩(Compacting): 对存活对象进行整理,消除内存碎片
- d. 分代(Generational): 根据对象的生命周期将内存分为不同的代(Generation),一般将堆分为新生代和老年代,根据对象存活时间的不同采用不同的回收算法