前言:
JVM是面试中必问到题,大部分应届生也只是在背八股文的时候才对JVM有所了解,我也是一样。
除了面试会问到之外,JVM也是初、中、高级程序员必备的技能(初级可能接触的少,不过也是为以后打下了基础)。学习JVM可以让程序员更深入的理解Java语言,对于排查问题也很重要。
一、什么是JVM?
Java虚拟机 (Java Virtual Machine),用来保证Java语言的跨平台。
Java虚拟机可以看做是一台抽象的计算机,如同真实的计算机那样,有自己的指令集和各种运行时内存区域。
Java虚拟机只与特定的二进制文件格式(class文件格式)有所关联,与Java语言没有必然联系。
Java虚拟机就是一台字节码翻译器,将字节码文件翻译成目标机(系统)对应的机器码,确保字节码文件能在各个系统正确运行。
二、JVM体系结构
如下图所示:
JVM体系结构分为:
- 类装载器子系统(类加载器)Class Loader SubSystem
- 运行时数据区
- 执行引擎
2.1、类加载器子系统
- 加载阶段(Loading),加载class文件
- 连接阶段(Linking):验证、准备、解析
- 初始化阶段(Initialization),初始化
2.2、运行时数据区
- 堆:存储Java对象的地方,保存了所有的对象实例和数组,也是GC管理的主要区域。
- 方法区:用于存储虚拟机加载的类信息、常量、静态变量、代码等数据。
- 虚拟机栈:每个线程在创建时都会创建一个虚拟机栈,虚拟机栈内部保存了一个个的栈帧,对应着一次次的方法调用。
- 本地方法栈:与虚拟机栈作用相似,区别在于虚拟机栈为虚拟机执行Java方法服务,而本地方法栈为虚拟机的Native方法服务。
- 程序计数器(pc寄存器):用于存储下一条指令的地址,由执行引擎读取下一条指令。
线程共享(堆、方法区),线程不安全的。
线程私有(虚拟机栈、本地方法栈、程序计数器),线程安全。
2.3、执行引擎
-
解释器:
主要作用是读取字节码,对字节码进行解释并逐一执行。解释字节码的速度较快,但是执行的速度较慢。如果一个方法被多次调用,它每次都要进行解释,这时候就需要即时编译器。 -
即时编译器(JIT编译器):
用来解决解释器的缺点,当它发现重复代码的时候,将采用即时编译器,编译整个字节码并将其更改为本地代码,使用本地代码直接用于重复的方法调用,提高系统的性能。 -
垃圾回收器:
收集或删除未被引用的对象,可以通过System.gc来触发垃圾回收,但是并不能保证执行,Java垃圾收集器只收集用关键字创建的对象(new出来的对象)。其他形式创建的对象,可以使用finalize方法清理。
三、堆
堆是用来存储对象的区域,堆是JVM中最大的一块内存区域。
特点:
- 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。
- 堆是用来存放对象实例的,因此也是垃圾收集器管理的主要区域。
- 堆的逻辑上划分为 “新生代” 和 “老年代”,新生代分为Eden区、ServivorFrom、ServivorTo三个区。
- 堆一般实现成大小是可扩展的,使用 “-Xms” 与 “-Xmx” 控制堆的最小与最大内存。
3.1、堆内存溢出
如果我们不断的去产生新的对象,而产生的对象又一直在被使用,这些对象就没办法被回收,长此以往下去,可能就会导致内存耗尽,也就是内存溢出
。
代码演示:
1、堆内存大小不够而造成的堆内存溢出。
public class Test1{
public static void main(){
int count = 0;
try{
List<String> list = new ArrayList<>();
String s = "test1";
for(int i=0; i<20; i++){
list.add(s);
s