JAVA内存区域(运行时数据区)

本文详细介绍了Java虚拟机的内存区域,包括程序计数器、虚拟机栈、本地方法栈、Java堆和方法区。每个区域的功能、作用以及在多线程环境下的工作原理进行了阐述,特别强调了每个栈帧中的局部变量表、操作数栈和动态连接。此外,还讨论了方法返回地址和异常处理,以及运行时常量池和垃圾回收在方法区的应用。最后,提到了本地方法栈为 Native 方法服务的特点和可能出现的内存溢出异常。
摘要由CSDN通过智能技术生成

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。

1、程序计数器

程序计数器,是一块较小的内存空间,可以看做当前线程所执行的字节码的行号指示器。

java文件编译成class字节码文件后,会生成一系列操作指令,每个操作指令都会有相应的指令地址(偏移地址),当字节码解释器器工作时,程序计数器会保存下一条指令要执行的指令的地址, 之后,执行引擎会从 程序计数器中,获得指令的地址,拿到操作指令,对其进行执行。

多线程执行时,若A线程挂起,B线程执行,B线程挂起,A线程继续执行,此时就需要通过A线程的程序计数器来恢复A线程指令的执行位置。为了保证线程切换执行后可以正确执行指令,因此每个线程都有一个独立的程序计数器,也就是说程序计数器是线程私有的。


java虚拟机栈(Java Virtual Machine Stack)
虚拟机栈也称为Java栈,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)。
1. Java虚拟机栈是线程私有的,它的生命周期与线程相同(随线程而生,随线程而灭)。
2. 栈帧用于存储包括局部变量表、操作数栈、动态连接、方法返回地址和一些附加信息。
3. 每一个方法被调用直至执行完毕的过程,就对应这一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表
局部变量表( Local Variables)是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。 这些变量可以是基本数据类型( int、char等)、对象引用、returnAdress类型(指向一条字节码指令地址)。
局部变量表所需的内存空间在编译期间完成分配,运行期间不会改变。数据在局部变量表中的存储空间以局部变量槽(Slot)来表示。一般一个局部变量槽32比特,64位长度的long和double类型的数据会占用两个变量槽,虚拟机真正使用多大内存空间来实现一个变量槽,完全由虚拟机的实现自行决定。
操作数栈(操作栈)
是一个先进后出的栈,用于保存计算过程中的中间结果,同时作为计算过程中变量临时的存储空间。 栈中的任何一个元素都可以是任意的Java数据类型,32bit的类型占用一个栈深度,64bit的类型占用两个栈单位深度。
操作数栈在方法的执行过程中,根据字节码指令往栈中写入数据或提取数据,即入栈和出栈操作。虽然栈是用数组实现的,但根据栈的特性,对栈中数据访问不能通过索引,而是只能通过标准的入栈和出栈操作来完成一次数据访问。
两个局部变量相加的过程
依次将需要进行操作的两个局部变量压入操作数栈,然后弹出栈顶数据放入存储局部变量表,如果对两个局部变量计算的话,再将该两个变量压入栈中,计算结果并放入栈顶,然后返回栈顶结果。
动态连接
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。Class 文件的常量池中存在大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数,这些符号引用一部分会在类加载阶段或第一次使用时转化为直接引用,这种转化成为静态解析。另一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。
方法返回地址
当一个方法开始执行后,只有两种方式可以退出这个方法:正常完成出口、异常完成出口。
一种是执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给上层方法的调用者,是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称为正常完成出口。
另一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是 Java 虚拟机内部产生的异常,还是代码中使用 athrow 字节码指令产生的异常,只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,这种称为异常完成出口。
一个方法使用异常完成出口的方式退出,是不会给上层调用者产生任何返回值的。 无论采用何种退出方式,在方法退出后都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的 PC 计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息。
方法退出的过程实际上就等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上次方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整 PC 计数器的值以指向方法调用指令后面的一条指令等。
附加信息
虚拟机规范允许具体的虚拟机实现增加一些规范里没有描述的信息到栈帧中,例如与调试相关的信息,这部分信息完全取决于具体的虚拟机实现。实际开发中,一般会把动态连接、方法返回地址与其他附加信息全部归为一类,成为栈帧信息。

本地方法栈(Native Method Stacks)
本地方法栈与 Java 虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的 Native 方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。
  • 本地方法栈是一个后入先出(Last In First Out)栈。
  • 由于是线程私有的,生命周期随着线程,线程启动而产生,线程结束而消亡。
  • 本地方法栈会抛出 StackOverflowError 和 OutOfMemoryError 异常。

Java堆(Java Heap)
  • Java堆是java虚拟机所管理的内存中最大的一块
  • Java堆被所有线程共享的一块内存区域
  • 虚拟机启动时创建java堆
  • Java堆的唯一目的就是存放对象实例。
  • Java堆是垃圾收集器管理的主要区域。
  • 从内存回收的角度来看, 由于现在收集器基本都采用分代收集算法, 所以Java堆可以细分为:新生代(Young)和老年代(Old)。 新生代又被划分为三个区域Eden、From Survivor, To Survivor等。无论怎么划分,最终存储的都是实例对象, 进一步划分的目的是为了更好的回收内存, 或者更快的分配内存。
  • Java堆可以处于物理上不连续的内存空间,逻辑上视为连续。对于大对象,出于实现简单、存储效率的考虑,多数虚拟机要求物理上连续。
  • Java堆的大小是可扩展的, 通过-Xmx和-Xms控制。
  • 如果堆内存不够分配实例对象, 并且对也无法在扩展时, 将会抛出outOfMemoryError异常。

方法区 (Method Area)
方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类的信息、常量、静态变量、即时编译器编译后的代码等数据。
即时编译器,是一个程序,该程序可以把Java的字节码转换成可以直接发送给处理器的指令。
  • 方法区看做是一块独立于Java堆的内存空间。
  • 方法区与java堆一样,是各个线程共享的内存区域。
  • 方法区在JVM启动的时候被创建,并且它的实际的物理内存空间中和Java堆区一样都可以是不连续的。
  • 方法区的大小,和堆空间一样,可以选择固定大小或者可扩展。
  • 方法区的大小决定了系统可以存储多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:OutOfMemoryError( 比如加载大量的第三方的jar包,等
  • 关闭JVM就会释放这个区域的内存。

运行时常量池(Runtime Constant Pool)
运行时常量池是方法区的一部分,用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放 。
字面量:String str =“str”;int i = 1;
符号引用就是某个变量在编译期的时候无法确定其内存地址。
String str = "Hello"; System.err.println(str); 第二个str在编译的时候就会编译成符号引用。

参考:

《深入理解Java虚拟机-JVM高级特性与最佳实践》周志明

https://www.136.la/shida/show-131489.html

程序计数器(关于java虚拟机内存的那些事)_young-youth的博客-CSDN博客_为什么程序计数器不会内存溢出

java堆内存详解 - 盛开的太阳 - 博客园

JVM 系列 - 内存区域 - 本地方法栈(四) - 简书

第 8 章 一 方法区的理解, 演变、方法区内部结构(运行时常量池)、方法区垃圾回收、运行时数据区总结_Guizy-CSDN博客

上述是本人学习Java虚拟机过程中整理的内容,若有不正之处还望指教。若有用到某位大神的文章内容或资料,还请留言,本小菜会备注上出处。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值