【Java JVM】运行时数据区

JVM 在执行 Java 程序的过程中会把它管理的内存分为若干个不同的数据区域, 这些区域有着各自的用途。
根据《Java虚拟机规范》中规定, JVM 所管理的内存大致包括以下几个运行时数据区域, 如图所示:
Alt 'JVM 运行时数据区'
这个运行时数据区被分为了 5 大块

  1. 方法区 (Method Area)
  2. 堆 (Heap)
  3. 虚拟机栈 (Virtual Machine Stacks)
  4. 本地方法栈 (Native Method Stacks)
  5. PC 寄存器 (Program Counter Register)

其中: 绿色部分是各个线程之间独享的部分, 而蓝色部分是所有线程共享的区域。

1 PC 寄存器 (Program Counter Register)

  1. 程序计数器是一块较小的内存分区, 你可以把它看做当前线程所执行的字节码的指示器(类似于, 记录了线程执行到了哪个位置, 下一步的位置)。在虚拟机的概念模型里, 字节码解释器工作时, 就是通过改变计数器的值来选择下一条需要执行的字节码指令。

  2. 程序技术器为线程私有, 每个线程都有它们各自的程序计数器, 这样在多线程的情况下, 线程之间的来回切换, 也能正确找到上次切换时执行的位置。

  3. 如果线程正在执行的是一个 Java 方法, 那么程序计数器记录的是当前线程正在执行的字节码指令的地址; 如果线程正在执行的是一个 native 方法, 则计数器值为空。

  4. 此内存区域是唯一一个 Java 虚拟机规范中没有规定任何 OutOfMemoryError (OOM) 情况的区域。

2 虚拟机栈 (Virtual Machine Stacks)

  1. 虚拟机栈是线程私有的, 它的生命周期与线程相同。

  2. 虚拟机栈可以看做是 Java 方法执行的内存模型: 每个方法执行的同时都会创建一个栈帧用于存储局部变量表, 操作数栈, 动态链接, 方法返回等信息。 一个 Java 方法从调用到执行完的过程, 就对应着一个栈帧从虚拟机栈入栈到出栈的过程。

  3. 局部变量表中存放了编译期可知的基本数据类型, 对象引用, returnAddress 类型 (指向了一条字节码指令的地址)。局部变量表里面的数据类型的存储空间是以槽 (Slot) 表示的, 64 位的 long 和 double 类型占 2 个槽, 其他的只占 1 个。局部变量表所需的内存空间在编译器就完成分配了, 运行期间不会改变局部变量表的大小, 既槽的个数是确定的 (但是一个槽的所占的空间大小, 则是有 JVM 自行实现的, 可以是 32b, 64b 等)。

  4. 在虚拟机栈中可能会出现两种异常:StackOverflowError 和 OutOfMemory。

4.1 如果线程请求的栈深度大于当前 JVM 所允许的深度, 会抛出 StackOverflowError 异常
4.2 虚拟机栈可以动态扩展, 当扩展时无法申请到足够的内存, 会抛出 OutOfMemory 异常

3 本地方法栈 (Native Method Stacks)

  1. 本地方法栈与虚拟机栈所发挥的作用是非常相似的, 其区别只是虚拟机栈为 JVM 执行 Java 方法服务, 而本地方法栈则是为虚拟机使用到的本地 (Native) 方法服务。

  2. 在 HotSpot 虚拟机中直接把本地方法栈和虚拟机栈合二为一。

  3. 同样的本地方法栈也会抛出 StackOverflowError 和 OutOfMemory。

4 堆 (Heap)

  1. JVM 只有一个堆, 同样的所有的线程共享这个堆, 他的生命周期同样和 JVM 一样。

  2. 堆主要存放的是类的实例和被分配的数组数据。

  3. 堆是 JVM 中内存最大的一块, 也是垃圾回收管理的主要区域, 堆在物理上是可以为不连续的内存空间, 只要逻辑上连续即可。

  4. 堆的实现是可以固定大小, 也可以是动态扩展的, 当堆的内存使用完了, 同样会抛出 OutOfMemoryError。

5 方法区 (Method Area)

  1. JVM 只有一个方法区, 所有的线程共享着这个唯一的方法区, 他的生命周期和 JVM 一样。

  2. 方法区用于存储已被 JVM 加载的类型信息, 常量, 静态变量, 即时编译器编译后的代码等数据。

  3. 方法区逻辑上是属于堆的一部分, 它却有一个别名叫作 “非堆” (Non-Heap), 用于区分堆 (Heap)。

  4. 方法区中, 垃圾回收比较少见, 但并不是不进行 GC, 这个区域的回收目标主要是针对常量池的回收和对类的卸载, 当方法区内存不足时, 会导致 OutOfMemoryError (OOM)。

  5. 《Java虚拟机规范》对方法区的约束是非常宽松的, 所以导致对不同的 JVM 对这个区域的实现有差异。比如: HotSpot 用永久代来实现方法区, 但是其他的 JVM 没有这种方式, 不存在永久代的概念的。

说到 HotSpot, 就不能不说不同 JDK 版本下, 方法区的实现和几个常量池的概念。

5.1 HotSpot 方法区的实现

在 JDK8 之前, HotSpot 方法区是通过永久代的方式实现的, 但是到了 JDK8, 方法区的实现改为元空间 (MetaSpace) 的方式, 同时从以前的堆空间移到了本地内存 (Native memory) 中。

Metaspace 的组成

  1. Klass Metaspace: 就是用来存 klass 的, klass 是我们熟知的 class 文件在 JVM 里的运行时数据结构, 这个区域不一定有的, 只有开启压缩指针, 同时 -Xmx (设定程序运行期间最大可占用的内存大小) 小于等于 32G (大于这个临界值, 会导致压缩指针关闭), 才会有这个区域
  2. NoKlass Metaspace: 专门来存 klass 相关的其他的内容, 比如 method, constantPool 等, 虽然叫做 NoKlass Metaspace, 但是也其实可以存 klass 的内容。当然了, 如果没有 Metaspace 的时候, klass 内容也会存储在这里。

上面说的临界值 32G 的确定, 可以看这里 聊一聊JAVA指针压缩的实现原理 (图文并茂, 让你秒懂)

5.2 常量池(Constant Pool)

在 JVM 中关于常量池的关键字有 class 常量池, 字符串常量池, 运行时常量池

5.2.1 class 常量池

这里的 class 常量池, 主要指的是 class 文件里面的常量池。
当我们的 .java 文件编译为 .class 文件后, .class 文件里面有一个常量池的项,
用于存放编译器生成的各种字面量 (Literal) 和符号引用 (Symbolic References)。

字面量: 各种常量, 如文本字符串, final 的常量值。
符号引用: 一组符号描述所引用的目标。

class 常量池的内容, 如图
Alt '常量池项包含的内容'
在 .class 文件中的样子大体如下

Constant pool:#1 = Methodref          #4.#13         // java/lang/Object."<init>":()V#2 = Methodref          #3.#14         // SymbolicReference.fn2:()V#3 = Class              #15            // SymbolicReference#4 = Class              #16            // java/lang/Object
5.2.2 字符串常量池 (String Constant Pool)

在 JVM 中有一个字符串常量池, 在我们平常创建字符串时, 会先到这个字符串池中查看是否已有相关的字符串了, 有的话直接使用这个字符串, 没有再创建然后加入到这个字符串常量池中。

在 HotSpot 里实现的字符串常量池功能的是一个 StringTable 类, 它是一个 Hash 表, 默认值大小长度是 1009 (JDK7 及后面的版本可以通过参数进行修改)。

这个 StringTable 在每个 HotSpot 的实例只有一份, 被所有的类共享。

当类加载到了 JVM 后, 类中涉及到的字符串常量, 在堆中生成字符串对象实例, 会将这些字符串对象实例的引用值存到字符串常量池中, 既存到 StringTable 中。
也就是我们的字符串常量具体的值是放在中的, 但是会有个引用指向它, 同时这个引用存放在 StringTable 中。

Java 有一道经典的题目

String s1 = new String("s1");

一共创建的多少个对象? 里面就涉及到对应的字符串是否已经在字符串常量池中存在。
具体的分析可以看一下这篇文章: 面试题系列第2篇:new String()创建几个对象?有你不知道的

5.2.3 运行时常量池 (Runtime Constant Pool)

当类加载到内存中后, JVM 就会将 class 文件的常量池中的内容存放到运行时常量池中。
运行时常量池相对于 class 文件常量池的另外一个重要特征是具备动态性

  1. class 文件常量池的符号引用存在运行时常量池中, 经过解析之后, 也就是把符号引用替换为直接引用
  2. 运行期间也可以将新的常量放入池中, 比如 String 的 intern() 方法

在 JDK8 之前 运行时常量池都是存放在方法区中, JDK8 及后面版本, 则将运行时常量池放到了堆中。

所以 HotSpot 在 JDK8 的时候, 运行时数据区是这样的
Alt 'JDK8 运行时数据区'

6 直接内存 (Direct Memory)

HotSpot 在JDK8 中, 将方法区的实现修改为了元空间的方式, 同时将元空间的存储移到了直接内存。

直接内存 (Direct Memory) 并不是虚拟机运行时数据区的一部分, 也不是《Java虚拟机规范》中定义的内存区域。

本机的直接内存的分配不会受到 Java 堆大小的限制, 但是会受到本机总内存的影响, 所以在内存不够的时候, 也会导致 OutOfMemoryError 异常。

7 参考

《深入理解Java虚拟机》- 周志明

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/232579.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu20.04.2 Mate 安装后基本初始设置要点笔记

序言&#xff1a; 有几款Linux比较稳定而且LTS长期支持&#xff0c;窗口也比较干净有特色&#xff0c;CentOS、Ubuntu、Debian、Mint 都是挺不错的OS&#xff0c;因为LTS&#xff0c;所以不像Rolling版那样改动频发&#xff0c;为长期应用提供了比较好的保障。下面是 Ubuntu20…

从Gitee克隆项目、启动方法

从gitee克隆VUE项目到本地后&#xff0c;不能直接运行&#xff0c;需要进行npm install安装node_modules文件夹里面的内容&#xff0c;因为在git上传的时候&#xff0c;一般都会过滤到node_modules中的依赖文件。 安装依赖以后&#xff0c;启动通过npm run serve启动项目出错。…

linux xxd命令(将文件或标准输入转换为hex(十六进制)和ASCII(美国信息交换标准代码)表示,或者从hex dump(十六进制转储)反向到二进制)

文章目录 Linux xxd命令安装xxd基本使用方法创建hex dump从hex dump恢复到二进制 命令选项疑难技术点解析在脚本中使用xxd从hex dump恢复数据 总结 Linux xxd命令 xxd是一个在Linux和UNIX系统中常用的工具&#xff0c;主要用于将文件或标准输入转换为hex&#xff08;十六进制&…

Python框架篇(6):FastApi-配置管理

提示: 微信搜索【猿码记】回复 【fastapi】即可获取源码信息~ 在这一篇文章中,对fastapi框架和pydantic进行了升级&#xff0c;然后就是各种不兼容&#xff0c;以后再也不敢轻易升级.... pydantic&#xff1a;从 1.10.11升级到 2.5.2&#xff0c;这里有坑&#xff0c;里面有很多…

蓝凌OA getLoginSessionId.html 信息泄露漏洞

文章目录 产品简介漏洞概述指纹识别漏洞利用修复建议 产品简介 蓝凌核心产品EKP平台定位为新一代数字化生态OA平台&#xff0c;数字化向纵深发展&#xff0c;正加速构建产业互联网&#xff0c;对企业协作能力提出更高要求&#xff0c;蓝凌新一代生态型OA平台能够支撑办公数字化…

SpringCloud微服务 【实用篇】| Docker镜像、容器、数据卷操作

目录 一&#xff1a;Docker基本操作 1. 镜像操作 镜像相关命令 2. 容器操作 容器相关命令 3. 数据卷&#xff08;容器数据管理&#xff09; 数据卷 操作数据卷 挂载数据卷 挂载的方式区别 前些天突然发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0…

【数据可视化】Pyecharts的实际使用

Pyecharts的实际使用 前言正文环境分析Prometheus数据处理Gauge图Bar图-横向如何整合进Flask中 附录 前言 一个多月前参加公司的一个产品会的时候&#xff0c;有和同事聊到日常巡检报表的一些东西&#xff0c;现在虽然项目上搭建的有监控平台、数据稽核平台、调度平台等业务系…

JavaWeb 学生信息管理系统

介绍 ServletMysqlJdbcjQuery 实现学生信息管理系统 学生 班级 教师 系统设置 登陆 软件架构 软件架构说明 基于ServletMysqlJdbcjQuery 实现学生信息的增删改查功能 文件目录声明 src/dao 数据库的增删改查功能src/filter 网页的过滤拦截功能src/model 登陆的实体对象信息…

Gazebo11更新安装

ROS Melodic版本安装的是Gazebo9&#xff0c;Gazebo 最新版本是11 dpkg -l | grep gazebo 出现的是 gazebo9 相关的插件&#xff0c;需要卸载全部插件。 sudo apt-get remove gazebo9 gazebo9-common gazebo9-plugin-base libgazebo9:amd64 libgazebo9-dev:amd64 ros-melodic…

从账户取款和存款的操作

public class Account {private double balance;public Account(double balance){super();this.balancebalance;}public Account(){super();}public void withdraw(double money) throws NotFullBalanceException{//取款if(money<balance){balance - money;}else{throw new …

flink 读取 apache paimon表,查看source的延迟时间 消费堆积情况

paimon source查看消费的数据延迟了多久 如果没有延迟 则显示0 官方文档 Metrics | Apache Paimon

Ring Co-XOR encryption based reversible data hiding for 3D mesh model

期刊&#xff1a;Signal Processing 作者&#xff1a;Lingfeng Qu et al. -- 摘要&#xff1a; 加密域可逆数据隐藏被广泛应用于云存储数字媒体的内容安全、隐私保护和便捷管理。然而&#xff0c;RDH-ED技术在三维网格模型载体中的应用研究仍处于起步阶段。为解决现有针对三…

美化Pandas数据图表

二、数据条显示 Excel条件格式里&#xff0c;有一个数据条显示方式&#xff0c;用以可视化表达数据大小。 Pandas Style方法中也有数据条的表达形式&#xff0c;用df.style.bar来实现。 还是用前面人口数据的例子&#xff0c;我们来看下如何操作数据条。 import pandas as …

初探 Reactor、Proactor 线程模型与 BIO、AIO、NIO

1 前言 工作中或者是技术上经常会遇到 I/O 、线程模型相关的问题&#xff0c;以及同步、异步、阻塞、非阻塞等各种基础问题&#xff0c;之前上学时候的概念认知总是模糊的&#xff0c;一知半解。趁这次了解希望能够更加深入的去了解这方面的知识&#xff0c;于是有了接下来这篇…

(7)nacos集群搭建方法-有状态负载

前期准备 1.准备好nacos镜像文件,上传至镜像仓库: 2.创建自己集群下-命名空间下的有状态负载: 3.填写工作负载名称、设置实例数量、选择自己的命名空间: 4.选择镜像(目前最新的是2.2.3):

yolov5单目测距+速度测量+目标跟踪(算法介绍和代码)

要在YOLOv5中添加测距和测速功能&#xff0c;您需要了解以下两个部分的原理&#xff1a; 单目测距算法 单目测距是使用单个摄像头来估计场景中物体的距离。常见的单目测距算法包括基于视差的方法&#xff08;如立体匹配&#xff09;和基于深度学习的方法&#xff08;如神经网…

[自动化运维工具]ansible简单介绍和常用模块

ansible 源操作主机功能 自动化运维&#xff08;playbook剧本yaml&#xff09; 是基于python开发的一个配置管理和应用部署工具&#xff0c;在自动化运维中&#xff0c;现在还是异军突起 ansible能批量配置&#xff0c;部署&#xff0c;管理上千台主机&#xff0c;类似于xshell…

使用vite搭建项目时,在启动vite后,浏览器显示页面:找不到localhost的网页

现象 在使用前端工具vite&#xff08;版本5&#xff09;&#xff0c;搭建vue3项目时&#xff0c;启动vite&#xff0c;浏览器显示页面&#xff1a;找不到localhost的网页, 起初怀疑是 未加参数 --host0.0.0.0,导致&#xff0c;后加上该参数后问题依旧 解决 将index.html页面…

中国的下一个风口:低代码开发

“中国的下一个风口是什么&#xff1f;低代码&#xff1f;你赞同吗&#xff1f;” 可以肯定的是&#xff0c;企业想站上未来产业的风口&#xff0c;”数字化“是必经之路。而低代码是企业向制高点突进的手段。产业的风口&#xff0c;就是产业发展的趋势&#xff0c;只有尽力站上…

Makefile的使用

在Linux中使用 make 命令来编译程序&#xff0c;特别是大程序&#xff1b;而 make 命令所执行的动作 依赖于 Makefile 文件。以下是最简单的Makefile文件&#xff1a; 首先&#xff0c;包含如下文件&#xff1a; Makefile文件内容如下所示&#xff1a; 然后&#xff0c;直接执行…