本文转载自微信公众号「码虫甲」,作者为码虫甲。深入理解JVM的内存区域划分。

2024-07-05 23:49:22 作者:6kYzQ!yIEmp_M6UkZ
请联系码虫甲公众号转载本文。Java文件是怎样运行的?

处理流程:

1. 将Java文件编译为.class字节码文件

2. 类加载器将字节码文件加载到JVM虚拟机中

3. JVM执行引擎开始执行加载的字节码文件

在整个程序执行过程中,JVM会分配一段空间用于存储程序运行期间所需的数据和相关信息,通常称为Runtime Data Area(运行时数据区),即我们所说的JVM内存。第二、运行时数据区包括哪些部分?

运行时数据区主要分为五个部分:方法区和堆属于线程共享区,可能存在线程安全问题;而Java栈、本地方法栈和程序计数器属于线程独享区,不存在线程安全问题。在JVM的调优过程中,主要是集中在优化堆和栈这两部分。在Java虚拟机中,方法区存储了每一个类的相关信息(包括类名、方法、字段信息)、静态变量、常量以及编译后的代码等。类加载器在将.class文件加载到运行时数据区时,首先将其放入方法区。除了类的字段、方法和接口等描述信息外,Class 文件中还包含一个常量池,用于存储编译期间生成的字面量和符号引用。方法区中一个非常关键的部分就是运行时常量池。它是每个类或接口的常量池在运行时的表示形式。当类和接口被加载到JVM后,相应的运行时常量池就被创建。并不仅有Class文件中的常量池中的内容可以被放入运行时常量池,并且在运行期间也可以将新的常量加入到运行时常量池中,例如通过String类的intern方法。

2. 堆内存和方法区都属于线程共享的区域,它们主要用于存储一些数据,比如对象实例、数组等。在Java中,堆是垃圾收集器管理的关键部分,在JVM中只有一个堆。

3.Java的堆栈

Java的堆栈也被称为虚拟机堆栈(Java虚拟机堆栈),它是Java方法执行的内存模型。Java栈中存储的是一个个的栈帧,每个栈帧对应一个被调用的方法。在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池的引用(运行时常量池的概念在方法区部分会有讨论)、方法返回地址(Return Address)和一些额外的附加信息。当一个线程执行一个方法时,会创建一个对应的栈帧,然后将这个栈帧推入栈中。当函数执行完毕后,就会将函数栈帧弹出。因此可以推断,当前线程执行的方法对应的栈帧一定位于Java栈的顶部。说到这里,大家应该就明白了递归方法为什么容易导致栈内存溢出,以及为什么程序员不需要管理栈区的空间(在Java中,程序员基本不需要担心内存分配和释放,因为Java自带垃圾回收机制),这些空间的分配和释放都由系统自动完成。所有编程语言都使得程序员无法透明地访问栈上的空间。以下图片展示了一个Java堆栈的模型:

局部变量表。根据名称就可猜到,相信大家都明白其功能了吧。它的作用在于储存方法里的局部变量,包括方法内声明的非静态变量以及函数形式参数。对于简单数据类型的变量,直接保存其数值;而对于引用类型的变量,则保存指向对象的引用。编译器在编译时确定局部变量表的大小,因此在程序执行过程中无法改变局部变量表的大小。对于

操作数栈这个概念,我相信学习过数据结构中栈的朋友们对于求解表达式值这个问题一定不会感到陌生,栈的一个典型应用就是用于表达式求值。考虑到线程执行方法过程实际上是持续执行语句的过程,其实质是在进行计算。因此可以这样说,所有程序中的计算过程都需要操作数栈的支持。

是指向运行时常量池的引用,因为在执行方法时可能需要使用类中的常量,所以需要有一个引用指向运行时常量池。

方法用于返回地址。当方法执行完毕后,它需要返回到之前调用它的位置。因此,在栈帧中需要保存方法返回地址。因为每个线程可能在执行不同的方法,所以每个线程都有自己的Java栈,彼此之间没有影响。

4.在Thread类的源码中,我们可以看到start()方法被native关键字修饰,并且没有方法体,这就是一种本地方法。本地方法栈和Java栈的功能和原理非常类似,唯一的区别在于Java栈用于执行Java方法,而本地方法栈则用于执行本地方法(Native Method)。在JVM规范中,并没有规定本地方法的具体实现方式和数据结构,虚拟机可以自由选择实现。在HotSpot虚拟机中,直接将本地方法栈和Java栈合并为一个栈。

5. 程序计数器\n程序计数器(Program Counter Register),亦称为PC寄存器。学过汇编语言的同学对程序计数器这个概念应该不会感到陌生。在汇编语言中,程序计数器是指CPU中的寄存器,用来保存当前执行指令的地址。当CPU需要执行指令时,会从程序计数器中获取当前指令所在的存储单元地址。然后根据该地址获取指令。获取指令后,程序计数器会自动加1,或者根据转移指针获取下一条指令的地址,如此循环直至执行完所有指令。尽管JVM中的程序计数器不同于汇编语言中的物理CPU寄存器,但是在逻辑上其功能与汇编语言中的程序计数器等同,它用于指示正在执行哪条指令。在JVM中,多线程通过线程轮流切换来获取CPU执行时间。因此,在任何时刻,一个CPU内核只会执行一条线程的指令。为了确保每个线程在切换后能够恢复到之前的程序执行位置,每个线程都必须有自己独立的程序计数器,且不得相互干扰,否则会影响程序的执行顺序。因此,可以这样说,程序计数器是属于每个线程私有的。在JVM规范中规定,如果线程执行的是非本地方法,程序计数器会保存当前执行指令的地址;如果线程执行的是本地方法,则程序计数器的值是未定义的。由于程序计数器中存储的数据大小在程序执行过程中不会改变,因此它是内存区域中唯一一个不会出现OutOfMemoryError的区域,而且在内存占用方面可以忽略不计。这是一个包含了x和n的字符串,中间包含了p。

在线咨询 拨打电话

电话

02088888888

微信二维码

微信二维码