文章目录
- 前言
- 一、本地方法栈(线程私有)
- 二、程序计数器(线程私有)
- 三、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)。