首页 > 其他分享 >2. 运行时数据区域

2. 运行时数据区域

时间:2023-12-21 21:12:36浏览次数:28  
标签:常量 数据 虚拟机 区域 线程 内存 JVM 方法 运行

运行时数据区域

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

  • JDK 1.7

  • JDK 1.8

1. 程序计数器(Program Counter Register)

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

字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成

为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储

  • 此内存区域是唯一一个在虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域

2. 虚拟机栈(Virtual Machine Stack)

虚拟机栈描述的是 Java 方法执行的线程内存模型。JVM 以方法作为最基本的执行单元,每个方法被执行的时候,JVM 都会同步创建一个 栈帧(Stack Frame) 用于存储局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息

每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程

一个线程中的方法调用链可能会很长,以 Java 程序的角度来看,同一时刻、同一条线程里面,在调用堆栈的所有方法都同时处于执行状态。而对于执行引擎来讲,在活动线程中,只有位于栈顶的方法才是在运行的,只有位于栈顶的栈帧才是生效的,其被称为当前栈帧(Current Stack Frame),与这个栈帧所关联的方法被称为当前方法(Current Method)

  • 在虚拟机规范中,对这个内存区域规定了两类异常状况
    • 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常
    • 如果虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出 OutOfMemoryError 异常

2.1 运行时栈帧结构

2.1.1 局部变量表(Local Variables Table)

局部变量表是一组变量值的存储空间,用于 存放方法参数和方法内部定义的局部变量

2.1.2 操作数栈(Operand Stack)

操作数栈也常被称为操作栈,主要作为 方法调用的中转站 使用,用于存放方法执行过程中产生的中间计算结果,计算过程中产生的临时变量也会放在操作数栈中

当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈操作

2.1.3 动态连接(Dynamic Linking)

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接

Class 文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池里指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或者第一次使用的时候就被转化为直接引用,这种转化被称为静态解析。另外一部分将在每一次运行期间都转化为直接引用,这部分就称为动态连接

2.1.4 方法返回地址

当一个方法开始执行后,只有两种方式退出这个方法,一种是 return 语句返回,另外一种是在方法执行的过程中遇到了异常,并且这个异常没有在方法体内得到妥善处理

无论采用何种退出方式,在方法退出之后,都必须返回到最初方法被调用时的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层主调方法的执行状态

  • 一般来说,方法正常退出时,主调方法的程序计数器的值就可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中就一般不会保存这部分信息

2.1.5 附加信息

JVM 规范允许虚拟机实现增加一些规范里没有描述的信息到栈帧之中,例如与调试、性能收集相关的信息,这部分信息完全取决于具体的虚拟机实现

3. 本地方法栈(Native Method Stack)

本地方法栈与虚拟机栈所发挥的作用是非常相似的,区别是虚拟机栈为虚拟机执行 Java 方法(即字节码)服务,而本地方法栈则是为虚拟机使用到的本地方法服务

  • 与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出 StackOverflowError 或 OutOfMemoryError 异常

4. 堆(Heap)

虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是 存放对象实例,Java 世界里几乎所有的对象实例都在这里分配内存

  • 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆

如果从分配内存的角度看,在堆中可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB),以提升对象分配时的效率

  • 根据 JVM 规范的规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的

堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的 Java 虚拟机都是按照可扩展来实现的(通过参数 -Xmx-Xms 设定)

  • 如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,虚拟机将会抛出 OutOfMemoryError 异常

4.1 堆内存结构

从回收内存的角度看,由于现代垃圾收集器大部分都是基于分代收集理论设计的,所以堆中经常会出现新生代、老年代、Eden 空间、From Survivor 空间、To Survivor 空间等名词,需要注意的是这种划分只是根据垃圾回收机制来进行的划分,不是 JVM 规范本身制定的。而且到了今天,也出现了不采用分代设计的新垃圾收集器,继续采用这种分法就不太严谨

面渣逆袭:JVM经典五十问,这下面试稳了! - Java 堆内存结构

5. 方法区(Method Area)

用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据

方法区其实是一个抽象的概念,永久代(Permanent Generation)与元空间(Metaspace)都是方法区的具体实现

  • 如果方法区无法满足新的内存分配需求时,将抛出 OutOfMemoryError 异常

5.1 为何用元空间替换永久代

  • 使用永久代更容易有内存溢出的问题。永久代有固定的大小上限,即使不设置也有默认大小,而元空间使用的是本地内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小
  • 有极少数方法,例如 String::intern() 会因永久代的原因而导致不同虚拟机下有不同的表现
  • JRockit、J9 等虚拟机本身就没有永久代的概念。当 Oracle 想将 JRockit 的优秀功能移植到 HotSpot 时,因为两者对方法区实现的差异而面临诸多困难,在 JDK1.6 时 HotSpot 团队就有放弃永久代,逐步改为采用本地内存实现方法区的计划。到了 JDK1.7 把原本放在永久代的字符串常量池、静态变量等移出,而到了 JDK1.8 完全废弃了永久代的概念,改用在本地内存中实现的元空间来代替,把 JDK1.7 中永久代还剩余的内容(主要是类型信息)全部移到元空间中

6. 运行时常量池(Runtime Constant Pool)

运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表,用于 存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中

  • 运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常

7. 直接内存(Direct Memory)

直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致 OutOfMemoryError 异常出现

本机直接内存的分配不会受到堆大小的限制,但是,既然是内存,则肯定还是会受到本机总内存(包括物理内存、SWAP 分区或者分页文件)大小以及处理器寻址空间的限制

  • 一般服务器管理员配置虚拟机参数时,会根据实际内存去设置 -Xmx 等参数信息,但经常忽略掉直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),从而导致动态扩展时出现 OutOfMemoryError 异常

8. 字符串常量池(String Constant Pool)

在日常开发过程中,字符串的创建是比较频繁的,而字符串的分配和其他对象的分配是类似的,需要耗费大量的时间和空间。JVM 为了提升性能和减少内存消耗针对字符串专门开辟的一块区域,主要目的是为了避免字符串的重复创建

  • JDK1.7 之前,字符串常量池存放在永久代中。JDK1.7 字符串常量池和静态变量从永久代移动了堆中

引用

标签:常量,数据,虚拟机,区域,线程,内存,JVM,方法,运行
From: https://www.cnblogs.com/holyholic/p/17920113.html

相关文章

  • JVM基础篇(三)-JVM结构-运行时数据区之栈帧
    栈帧栈帧的内部结构每个栈帧中存储着:局部变量表(LocalVariables)操作数栈(operandStack)(或表达式栈)动态链接(DynamicLinking)(或指向运行时常量池的方法引用)方法返回地址(ReturnAddress)(或方法正常退出或者异常退出的定义)一些附加信息并行每个线程下的栈都是私有的,因此每个线程都有自己各......
  • 【专题】2023零售连锁品牌数字化运营研究及策略报告PDF合集分享(附原数据表)
    原文链接:https://tecdat.cn/?p=34632原文出处:拓端数据部落公众号在2022年,由于疫情的短期影响,消费市场受到明显扰动,服装和家居行业出现了明显的下滑。过去三年,数字化是零售行业实现降本增效的关键手段。然而,随着2023年的消费复苏,线下实体门店开始获得“修复式”增长,零售品牌的数......
  • Week1——STL 与基础数据结构专题训练
    https://blog.csdn.net/qq_46025844/article/details/127948957 实训概要实训专题STL与基础数据结构专题训练实训目的掌握STL常用的算法、容器、容器适配器的使用方法。能够利用STL的算法、容器、容器适配器求解问题。题目列表A:摘苹果B:立方和C:计算个数D:后缀表达式的值E:做蛋糕......
  • 【专题】2022中国预制菜数字消费报告PDF合集分享(附原数据表)
    报告链接:https://tecdat.cn/?p=33388原文出处:拓端数据部落公众号近年来,中国的预制菜行业迅速发展,已成为消费者生活中不可或缺的一部分。研究报告显示,预制菜行业在美国和日本等国家已经发展了很长时间,与中国市场相比,中国的预制菜市场仍有巨大的增长潜力。预制菜行业的蓬勃发展主......
  • 伪造网页数据(每个文件夹抽指定数量的图片并重命名)
    (承接"公共标,特殊标"的结构)1.对于指定父目录结构,每个文件夹抽指定数量的随机图片。importosimportshutilimportrandomdeffind_image_folders(parent_directory):#查找包含图片的文件夹image_folders=[]forroot,dirs,filesinos.walk(parent_direc......
  • CloudCanal x Debezium 打造实时数据流动新范式
    简述Debezium是一个开源的数据订阅工具,主要功能为捕获数据库变更事件发送到Kafka。CloudCanal近期实现了从Kafka消费Debezium格式数据,将其同步到StarRocks、Doris、Elasticsearch、MongoDB、ClickHouse等12种数据库和数仓,补全其数据到达能力。本文将先简单介绍该项......
  • 羚通视频智能分析平台:安防视频汇聚,危险区域行人入侵算法识别与检测预警
    随着科技的不断发展,安防领域也在不断地进行创新和升级。羚通视频智能分析平台,作为一款领先的安防视频汇聚平台,凭借其强大的功能和高效的性能,已经成为了许多企业和机构的首选。本文将详细介绍羚通视频智能分析平台在危险区域行人入侵算法识别和检测预警方面的应用。一、羚通视频智......
  • mongo如何使用脚本更新数据
    前言数据更新是我们日常操作数据库必不可少的一部分,下面这篇文章就给大家分享了操作MongoDB数据更新的一些干货,对大家具有一定的参考学习价值,一起来学习学习吧。常用的函数update(,,,),其中表示筛选的条件,是要更新的数据updateMany()更新所有匹配到的数据upsertupsert是一个布......
  • Spring mvc中前后端数据交互的方式
    现在IT行业ssm框架用的还是挺多的,今天给大家分享一下springmvc中前后的数据交互的方式:后端➡前端在SpringMVC中这主要通过Model将数据从后端传送到前端,一般的写法为:@RequestMapping(value="/index",method=RequestMethod.POST)publicStringindex(Modelmodel){String......
  • 【数据结构】第二章——线性表(2)
    线性表的顺序表示导言大家好,很高兴又和各位见面啦!!!在上一个篇章中,我们简单了解了一下线性表的基础知识以及一下重要的术语。在今天的篇章中我们将来开始正式介绍线性表的顺序存储——又称顺序表。我们将会在本章介绍什么是顺序表,对于顺序表的操作我们又应该如何实现。接下来,我们就来......