JVM内存结构和虚拟机栈
- 起始
- 简介
- 内存结构
- 虚拟机栈
- 程序计数器是用来做什么的
- 为什么会有native方法?
- 为什么会有本地方法栈
- 总结
起始
学会看官网 JDK1.8参数
简介
java文件被编译为class文件,运行时,class文件被classLoader读取,大多数时候会被解释器解释为机器码,然后被操作系统执行,但是也会被JNI通过即时编译编译为机器码,最后被操作系统执行
大多数
JAVA源文件
class文件
classLoader
字节码解释器
JNI即时编译
机器码
操作系统
内存结构
内存结构
运行时数据区
堆外内存
线程私有
线程共享
虚拟机栈 -Xss
本地方法栈
程序计数器
堆 -Xmx -Xmx
方法区
JVM运行时数据区:
图源:King老师
虚拟机栈
图源:King老师
方法调用时创建栈帧,并压入虚拟机栈中,当方法执行完毕时,栈帧从虚拟机栈中出栈。主要是做方法的出栈与入栈,同时,虚拟机栈是有大小限制的,可以通过-Xss来设置虚拟机栈的大小。在64位机器上,这个参数的默认值是1m,官方解释说明如下:
- 栈溢出:一般是方法调用过深,常见于递归调用
- 内存溢出:当线程创建过多时,每个线程会占用-Xss大小的内存,最后如果内存不足,也会OOM
理解:虚拟机栈中存放是栈针,方法被调用时,创建栈针并压入虚拟机栈中,栈帧分为四个组层部分,分别是局部变量表、操作数栈、动态连接和完成出口
栈帧
局部变量表
操作数栈
动态连接
完成出口
- 局部变量表
局部变量表中存储当前执行的方法中创建的8大基本数据类型,对自身的引用以及对堆中对象的引用 - 操作数栈
如果JVM的执行引擎相当于计算器的CPU的话,那么操作数栈相当于CPU的寄存器,操作数栈中存放了当前正在执行的指令以及结果(比如加法运算会先出栈再入栈)操作数栈中的命令会被JVM执行引擎执行
引申
如果读懂了字节码,就理解了一些优化的原则,比如
int i=0;
int y=10;
int z=(i+y)*100;
return z;
和
int i=0;
int y=10;
return (i+y)*100;
在效率上是不同的,因为int z=的时候,需要在操作数栈中先istore再iload最后再return,但是后面的方式节省了这两个指令
程序计数器是用来做什么的
因为CPU是采用时间划片的方式轮询各个任务的,在程序计数器中记录了当前CPU在当前线程当前方法中执行的字节码的地址
为什么会有native方法?
借鉴了C C++的一些类库,而且Java在初期也没有那么多的类库
为什么会有本地方法栈
虚拟机规范,但是HotSpot将本地方法栈和虚拟机栈合并了
总结
- 线程私有的区域有三个:本地方法栈、虚拟机栈、程序计数器,其中,程序计数器永远不会OOM,因为程序计数器只是保存了当前CPU执行到的方法的指令的地址。
- 虚拟机栈中存的是栈帧,每次方法调用都会产生栈帧并压入栈中。栈帧由四部分组成:动态连接,完成出口,操作数栈,局部变量表
2.1 局部变量表:在局部变量表中,记录了当前方法中创建的8大基本类型的变量以及对象的引用
2.2 操作数栈:操作数栈中存储的是当前CPU要执行的指令,相当于PC的寄存器