JVM是什么?
JVM,即Java Virtual Machine,即Java虚拟机。
虚拟机是什么?
模拟出一台和真实物理电脑行为几乎一样的虚拟电脑的软件。(JVM是进程虚拟机,不模拟硬件,只模拟一套自定义虚拟指令集,专门用来跑编程语言代码,和物理CPU无关)
为什么要模拟真实物理电脑?
解决Java程序跨平台运行的问题。
什么是跨平台运行?
一份软件或程序代码不需要修改源码、不用重新编译,就能在多种不同操作系统、不同 CPU 硬件架构上直接正常运行。
为什么需要跨平台运行?
多平台需要搭建多套编译环境,维护成本极高;第三方依赖极易出现编译失败、兼容性问题;给普通用户使用时,用户根本不会编译;版本分发、升级维护极其繁琐。
为什么能解决Java程序跨平台运行问题?
C/C++ 源码编译后直接生成当前 CPU + 操作系统的原生机器码程序。
Java源码编译生成在JVM上执行的字节码文件。
综上所述
JVM是用于执行Java字节码的虚拟机。
另外,它还提供类加载、内存管理、垃圾回收等运行时支持。
接下来看JVM是如何工作的
真正的计算机有CPU、内存、寄存器、程序计数器、指令执行,作为虚拟机,JVM里也有程序计数器(PC)、栈(Stack)、堆(Heap)、方法区(Method Area)、执行引擎(Execution Engine)。
JVM运行流程
前置工作:Java源代码编译成class字节码文件(javac执行)。
- 类加载器读取并解析 .class 文件,在运行时数据区创建类的运行时数据结构;
- 执行引擎读取这些字节码,通过解释执行或 JIT 编译生成机器码,最终由 CPU 执行(在这个过程中,垃圾回收也在工作)。
其中需要详细了解一下运行时数据区和垃圾回收。
运行时数据区
运行时数据区,即JVM内存结构,包括:程序计数器、虚拟机栈、本地方法栈、堆、方法区。
1、程序计数器:记录当前线程正在执行的字节码指令的地址
2、虚拟机栈:
- 每次函数调用都会有一个对应的栈帧被压入(栈帧存储方法参数、方法内局部变量、方法放回地址)
- 方法递归过多会导致java.lang.StackOverflowError 栈内存溢出
3、本地方法栈:调用本地函数时,压入对应栈帧(以native修饰的方法就是本地方法,本地方法是C/C++编写的,Java通过接口调用,Java通过本地方法调用操作系统底层功能)
4、堆:保存对象实例(垃圾收集器管理的主要区域)
5、方法区:
- 存储类信息、常量、静态变量
- JDK7方法区的实现叫:永久代,占用的是堆的内存空间,大小固定
- JDK8方法区的实现叫:元空间,占用的是本地内存的空间,大小自动调整
垃圾回收
- 是什么?
自动管理内存的一种机制。自动释放不再被程序引用的对象所占用的内存。
判断对象是否还被引用:可达性分析算法
- 垃圾回收算法
- 标记清除:标记存活对象(被引用对象)和垃圾对象(不被引用对象),回收垃圾对象;
- 标记整理:标记存活对象和垃圾对象,将存活对象向一端移动,清理边界以外的内存空间;
- 复制法:内存分为大小相等的两块,只使用其中一块,这一块使用完之后,将存活对象复制到另一块内存中,清理这一块的所有内存空间;
| 优点 | 缺点 | |
| 标记清除 | 回收速度快 | 会产生内存碎片 |
| 标记整理 | 不会产生内存碎片 | 效率较低 |
| 复制法 | 不会产生内存碎片且较为高效 | 内存缩小为原来的一半 |
- 分代垃圾回收
一般不单独使用某一种垃圾回收算法。
将堆分为新生代和老年代;
新生代中存储存活率低的对象,采用复制法(频繁回收,要高效一点);
老年代中存储存活率高的对象,采用标记整理法(很久回收一次,效率低一点可以接受)。