文章目录
- 前言
- 一、本地方法栈(线程私有)
- 二、程序计数器(线程私有)
- 三、Java虚拟机栈(线程私有)
- 四、堆(线程共享)
- 五、方法区(元数据区)
前言
JVM
是
Java 运行的基础,也是实现一次编译到处执行的关键。那么
JVM 是如何执行的呢
?
程序在执行之前先要把
java
代码转换成字节码(
class
文件),
JVM
首先需要把字节码通过一定的方式
类加载器(
ClassLoader
)
把文件加载到内存中
运行时数据区(
Runtime Data Area
)
,而字节码文件是 JVM
的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器
执
行引擎(
Execution Engine
)
将字节码翻译成底层系统指令再交由
CPU
去执行,而这个过程中需要调用其他语言的接口 本地库接口(
Native Interface
)
来实现整个程序的功能。
总结来看,
JVM
主要通过分为以下
4
个部分,来执行
Java
程序的,它们分别是:
1.
类加载器(
ClassLoader
)
2.
运行时数据区(
Runtime Data Area
)
3.
执行引擎(
Execution Engine
)
4.
本地库接口(
Native Interface
)
JVM 运行时数据区域也叫内存布局,它由以下
5
大部分组成:本地方法栈、程序计数器、虚拟机栈、堆区、元数据区(方法区)。
什么是线程私有
?
由于
JVM
的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现,因此在任何一个确定的时
刻,一个处理器
(
多核处理器则指的是一个内核
)
都只会执行一条线程中的指令。因此为了切换线程后能
恢复到正确的执行位置,每条线程都需要独立的程序计数器,各条线程之间计数器互不影响,独立存
储。我们就把类似这类区域称之为
"
线程私有
"
的内存。
一、本地方法栈(线程私有)
本地方法栈和虚拟机栈是类似的,只不过
Java
虚拟机栈是给
JVM
使用的,而本地方法栈是给本地方法使用的。
二、程序计数器(线程私有)
内存中最小的区域,它保存了下一条执行的指令的地址在哪。程序想要运行,JVM就得把字节码加载起来,放到内存中。程序就会一条一条把指令从内存中取出来,放到CPU上执行,那么也就要随时记住当前执行到哪一条了。CPU是并发式的执行程序的。操作系统是以线程为单位来进行调度执行的,每个线程都得记录自己的执行的位置,所以每个线程都会有一个程序计数器。
三、Java虚拟机栈(线程私有)
Java
虚拟机栈的作用:
Java
虚拟机栈的生命周期和线程相同,
Java
虚拟机栈描述的是
Java
方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,
用于存储局部变量表、操作数栈、动态链接、方法出口等信息。咱们常说的堆内存、栈内存中,栈内存指的就是虚拟机栈。
1.
局部变量表: 存放了编译器可知的各种基本数据类型
(8
大基本数据类型
)
、对象引用。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在执行期间不会改变局部变量表大小。简单来说就是存放方法参数和局部变量。
2.
操作栈:每个方法会生成一个先进后出的操作栈。
3.
动态链接:指向运行时常量池的方法引用。
4.
方法返回地址:
PC
寄存器的地址。
栈里面主要是涉及一些局部变量和方法调用信息,比如:方法调用的时候,每次调用一个新的方法,都会涉及到“入栈” 操作,每次执行完毕后就会“出栈”。
四、堆(线程共享)
堆的作用:程序中创建的所有对象都在保存在堆中。一个进程只有一份,多个线程共用一个堆,堆也是内存空间中最大的区域。比如:new出来的对象,就是在堆中。那么对象的成员变量也自然在堆中了。(局部变量在栈上,成员变量和new的对象在堆上)
void fun(){
String s = new String();
}
这个操作在进行的时候,s 和 new String 这里是两个不同的东西,s 是一个引用类型的变量,是局部变量,在栈上,那么new String对象的本地是在堆上的。
五、方法区(元数据区)
方法区的作用:用来存储被虚拟机加载的类信息、常量、静态变量、
即时编译器编译后的代码等数据的。
在《
Java
虚拟机规范中》把此区域称之为
“方法区”
,而在
HotSpot
虚拟机的实现中,在
JDK 7
时此区域叫做永久代(PermGen
),
JDK 8
中叫做元空间(
Metaspace
)。