【从零开始学习JVM | 第五篇】快速了解运行时数据区

前言:

        当谈论 Java 程序的运行机制时,JVM(Java 虚拟机)的运行时数据区是一个必不可少的话题。JVM 运行时数据区是 Java 程序在运行过程中分配内存和管理数据的重要区域,它包括了方法区、堆、虚拟机栈、程序计数器和本地方法栈等部分。了解 JVM 运行时数据区不仅有助于理解 Java 程序的运行原理,还有助于优化程序的性能和解决一些与内存管理相关的问题。本文将从各个方面介绍 JVM 运行时数据区的结构和作用,希望能够为读者提供全面深入的了解。

目录

前言:

运行时数据区:

程序计数器:

Java虚拟机栈:

本地方法栈:

堆: 

方法区: 

杂项知识点:

总结:


 

 

运行时数据区:


 

接下来我详细的讲解一下每一部分的作用:

程序计数器:

程序计数器(Program Counter Register)是 JVM(Java 虚拟机)中的一块较小的内存区域,它是线程私有的,即每个线程都有自己的程序计数器。程序计数器的作用是指示当前线程正在执行的虚拟机字节码指令的地址或者下一条需要执行的指令地址

以下是程序计数器的一些重要作用:

  1. 指示下一条指令的地址:程序计数器存储着当前线程正在执行的虚拟机指令的地址,当线程被调度并恢复执行时,JVM 将根据程序计数器中的地址来获取下一条需要执行的指令。

  2. 线程切换时保持状态:由于程序计数器是线程私有的,因此在线程切换时,程序计数器中的值会被保存和恢复。这保证了线程在恢复执行时能够继续执行之前的指令,而不会出现混乱。

  3. 支持线程中断和恢复:程序计数器的状态可以用来支持线程的中断和恢复机制。当一个线程被中断或者阻塞后又恢复执行时,程序计数器可以确保线程能够从中断前的地方继续执行,而不会跳转到其他位置。

  4. 处理异常跳转:程序计数器还用于处理异常跳转,例如在发生异常时,程序计数器可以指示 JVM 跳转到异常处理代码的指令地址。

在 Java 虚拟机规范中,程序计数器被定义为 JVM 中的一部分,并且针对程序计数器的操作都是 JVM 指令集中的一部分。程序计数器的大小是固定的,且不会发生内存溢出或内存泄露的情况,因为它不涉及到对象的分配或垃圾回收

Java虚拟机栈:

Java 虚拟机栈(JVM Stack)是 Java 虚拟机(JVM)中的一块重要内存区域,用于存储方法的局部变量、操作数栈、动态链接、返回地址以及方法出口等信息。每个线程在创建时都会在虚拟机栈中分配一个栈帧(Stack Frame),每当线程调用一个方法时,JVM 都会在虚拟机栈中创建一个对应的栈帧,用于存储该方法的相关信息

所谓的栈帧,就是一个保存方法基本信息的容器。

以下是JAVA虚拟机栈的一些重要特点和作用:

  1. 线程私有的数据区域:与方法区和堆不同,Java 虚拟机栈是线程私有的数据区域,意味着每个线程在运行时都拥有自己的虚拟机栈,用于存储线程独享的方法调用信息。

  2. 栈帧的结构:每个栈帧包含局部变量表(Local Variable Table)操作数栈(Operand Stack)动态链接(Dynamic Linking)方法返回地址额外的附加信息。局部变量表用于存储方法的参数和局部变量,操作数栈用于存储方法执行过程中的操作数,动态链接用于指向当前方法在运行时常量池中的方法引用,而方法返回地址用于存储方法调用后的返回地址。
  3. 栈帧的入栈与出栈:当线程执行方法调用时,对应的栈帧被压入虚拟机栈,当方法执行结束后,该栈帧被弹出栈。这种入栈与出栈的操作是基于方法的调用和返回关系进行的。

  4. 支持方法的递归调用:虚拟机栈的存在支持了方法的递归调用,每次递归调用都会在虚拟机栈中创建一个新的栈帧,以便存储方法的局部变量和执行信息。

  5. 栈深度限制:JVM 使用虚拟机栈来管理方法的调用和返回,因此虚拟机栈的深度是有限制的。如果方法调用的层次太深,虚拟机栈会发生栈溢出(StackOverflowError)。

Java 虚拟机栈在程序执行期间起着至关重要的作用,它不仅存储方法的局部变量和执行信息,还支持了方法的调用和返回。而JAVA虚拟机栈如果栈帧过多,占用内存超过栈内存可以分配的最大大小就会出现内存溢出。

本地方法栈:

本地方法栈(Native Method Stack)是 Java 虚拟机(JVM)中的一块内存区域,用于支持执行 Java 虚拟机调用本地(Native)方法时的数据结构。与虚拟机栈类似,本地方法栈也是线程私有的,每个线程都有自己的本地方法栈,用于执行本地方法时的方法调用和返回。

以下是本地方法栈的一些重要特点和作用:

  1. 支持本地方法调用:本地方法栈可以理解为虚拟机栈用于执行本地方法的部分。当 Java 虚拟机调用本地方法时,本地方法栈会记录该调用的信息,包括参数、局部变量等。

  2. 与虚拟机栈的区别:虚拟机栈主要用于执行 Java 方法时的数据结构,而本地方法栈用于执行本地方法时的数据结构。两者在结构上存在类似之处,但在功能上有明显的区别。

  3. 本地方法栈的深度限制:类似于虚拟机栈,本地方法栈也有一定的深度限制。在执行本地方法调用时,如果本地方法栈的深度超出了限制,则会导致栈溢出错误。

  4. 安全性和性能:本地方法栈的存在主要是为了提高 Java 虚拟机与本地方法库的安全性和性能,使得 Java 虚拟机能够与本地代码进行无缝集成。

需要注意的是,和虚拟机栈一样,本地方法栈也属于 Java 虚拟机规范中定义的一部分,不同的虚拟机对本地方法栈的大小和结构可能略有差异,但其作用和功能是相似的。

总体来说,本地方法栈是 Java 虚拟机用于支持本地方法调用的重要内存区域,通过了解本地方法栈的结构和作用,可以更好地理解 Java 虚拟机与本地方法库的交互过程,提高代码的运行效率和安全性。

在Hotspot虚拟机中,Java虚拟机栈和本地方法栈实现上使用了同一个栈空间


堆: 

是用于动态内存分配的一块内存区域,用来存储对象实例和数组。在 Java 中,所有的对象实例和数组都在堆上分配内存。堆内存支持动态分配和释放,通过堆上的内存分配指针进行分配。这意味着对象可以在程序运行时动态创建,并且可以根据需要动态调整堆的大小。堆内存在 Java 虚拟机中受到垃圾回收器的管理,用于回收不再使用的对象,释放其占用的内存。Java 的垃圾回收机制主要针对堆内存进行,以确保内存的合理利用和程序的稳定性。

堆的结构划分:

  1. 新生代(Young Generation):用于存放新创建的对象。通常被划分为 Eden 区和两个 Survivor 区。大部分对象在这里被创建,然后经过几轮垃圾回收后如果仍然存活,则会被移到老年代。

  2. 老年代(Old Generation):用于存放存活时间较久的对象,即由新生代经过多次垃圾回收后依然存活下来的对象。

  3. 永久代/元空间(PermGen/Metaspace):在较早的 Java 版本中使用永久代(PermGen)来存放类的元数据、常量池等信息,但在较新的版本中,使用元空间(Metaspace)来代替。这部分内存主要用于存放类和方法的元信息,以及一些静态的数据。

堆内存的划分:

  1. Used(已使用:表示当前已经被使用的堆内存大小,即已经被分配给对象实例和数组的内存空间的大小。

  2. Total(总量):表示当前堆内存的总大小,即JVM当前所分配的堆内存的总量。包括已使用的内存和未被使用的内存。

  3. Max(最大值):表示堆内存的最大可用空间大小,即JVM所能申请到的最大堆内存大小。

而在实际业务中,我们会直接把Total设置为和Max一样的大小,这样避免了申请并分配内存时间上的开销。同时也不会出现内存过剩后堆收缩的情况。堆也是可以溢出的。

方法区: 

方法区(Method Area)是Java虚拟机(JVM)的一个重要组成部分,它用于存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在虚拟机规范中,方法区是线程共享的。

方法区在虚拟机启动时被创建,并且是一块连续的内存区域。它的大小可以固定,也可以根据需要进行动态扩展。在HotSpot虚拟机中,方法区的大小是固定的,可以通过设置JVM参数来调整。

方法区主要存储以下内容:

  1. 类信息:包括类的完整结构、字段、方法、继承关系、接口等。

  2. 运行时常量池:每个类都有一个运行时常量池,在方法区中进行存储。它包含了字面量和符号引用。在早期,字符串常量池是运行时常量池的一部分,而在后续将二者进行了拆分

  3. 静态变量:所有类的静态变量都存放在方法区中。

  4. 即时编译器编译后的代码:JVM会将热点代码进行即时编译,并将生成的本地机器码存储在方法区中。

以下是方法区的一些重要特点和作用:

  1. 存储元数据:方法区主要用于存储类的元数据、常量、静态变量以及类中的符号引用等信息。这些信息在类加载时被存储在方法区中,对所有实例对象都是共享的。

  2. 无需手动内存管理:方法区不需要像堆内存(heap)一样进行垃圾回收。这是因为方法区中存储的数据并不像堆内存中的对象一样动态地创建和销毁,而是在类加载时确定并且通常在程序运行过程中保持不变。

  3. 永久代到元空间的变迁:在较老的JVM版本中,方法区通常被实现为永久代(PermGen)。但是从Java 8开始,永久代被元空间(Metaspace)所取代。元空间不再受到默认的最大永久代大小的限制,而是根据系统内存动态扩展。

  4. 动态性:与永久代不同,元空间的大小并不受默认设置或者-Xmx参数的限制,它可以根据应用的实际需要动态变化。

  5. 类信息存储:方法区将类的结构信息、即时编译的代码、常量池、静态变量等存储在内存中,这些数据对于程序执行过程中的类加载、方法调用等起着关键作用。

虽然历代设计中方法区的空间一直很大,但是他仍然有内存溢出的风险。

杂项知识点:

1.一个字符对象如何判断是存储到字符串常量池还是堆中?

在Java中,字符串对象有可能存储在堆内存中,也有可能存储在字符串常量池中,这取决于字符串对象创建的方式。

  1. 字符串常量池: 当使用字面量形式创建字符串对象时,例如 String s = "Hello",这个字符串对象会被存储在字符串常量池中。字符串常量池是Java堆内存中的一部分,用于存储字面量形式创建的字符串对象,这样的设计可以避免重复存储相同内容的字符串。

  2. 堆内存: 当使用 new 关键字显式创建字符串对象时,例如 String s = new String("Hello"),这个字符串对象会被存储在堆内存中。这种方式会在堆内存中创建一个新的字符串对象,即使字符串内容在常量池中已经存在。

因此,字符串对象既可以存储在堆内存中,也可以存储在字符串常量池中,取决于字符串对象的创建方式。需要注意的是,通过 intern() 方法可以将堆内存中的字符串对象手动放入字符串常量池中

总结:

JVM(Java虚拟机)运行时数据区是Java程序执行过程中存储和管理数据的内存区域,它被划分为多个不同的区域,包括方法区、堆、虚拟机栈、本地方法栈和程序计数器等。

首先,方法区(在Java 8及之前称为永久代)存储每个类的结构信息、静态变量、常量以及编译后的方法字节码。它在运行时可以被多个线程共享,是被所有线程共享的内存区域之一。

其次,堆是存储对象实例和数组的内存区域,它是Java程序中最常用的数据结构之一。堆的特点是可以动态分配内存,当程序运行时可以动态地创建对象实例,而且它是所有线程共享的内存区域。

虚拟机栈用于存储线程执行方法的局部变量、操作数栈、动态链接、方法出口等信息。每个方法执行的同时都会创建一个栈帧用于存储局部变量和操作数,而栈帧则会随着方法执行的结束而被销毁。

本地方法栈则与虚拟机栈类似,不同在于虚拟机栈是为Java方法服务,而本地方法栈则是为native方法(使用C、C++等语言编写的方法)服务。

最后,程序计数器是当前线程所执行的字节码的行号指示器,它在多线程环境下为每个线程都分配一个独立的程序计数器,用于记录当前线程执行的位置,是线程私有的内存区域。

总的来说,JVM运行时数据区域在Java程序执行过程中起着至关重要的作用,通过合理管理这些数据区域,可以优化程序的性能和内存的利用,也有助于理解Java程序的执行机制和内存管理原理

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

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

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

相关文章

Linux---日志管理

本章主要介绍Linux中的日志管理 了解rsyslog是如何管理日志的查看日志的方法 日志管理简介 工作当中的日志,特指硬件和软件的日志,管理员可以通过它来检查错误发生的原因,或者寻找受到攻击时攻击者留下的痕迹。日志管理包括管理系统日志、应…

智能外呼常见场景有哪些?

智能外呼常见场景是什么? 智能外呼在各种场景下都有应用,以下是一些常见的场景: 营销推广 通过智能外呼向潜在客户进行产品或服务的宣传和推广,收集客户对产品或服务的反馈。根据客户的反馈自动调整宣传策略,从而提…

2023年全国职业院校技能大赛“ 信息安全管理与评估” 测试题3

一、 单选题 ( 每题 2 2 分,共 共 5 35 题,共 共 0 70 分) 1、脏数据是指( )。 A、 不健康的数据 B、 缺失的数据 C、 多余的数据 D、 被撤销的事务曾写入库中的数据 2、滥发各种广告和虚假信息传播计…

mac本地部署stable-diffusion

下载Homebrew /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" ①输入“1”选择中科大版本,然后输入Y(YES),直接输入开机密码(不显示)然后回车确认,开始下载 ②…

LeeCode每日刷题12.7

移除元素 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出…

小航助学2023年6月GESP_Scratch二级真题(含题库答题软件账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统(含题库答题软件账号 单选题3.00分 删除编辑附件图文 答案:D 第1题高级语言编写的程序需要经过以下( )操作,可以生成在计算机上运行的可执行代码。 A、编辑B、…

关于popen执行命令无效,但是手动命令行执行有效的问题

问题:今天通过sshpass和scp拷贝其他芯片文件的时候发现代码里面通过popen执行的命令并没有把文件拷贝过来,然后手动执行相同的命令可以拷贝过来。 原因: popen和system可能检测不到用户的环境变量,导致找不到sshpass和scp命令&am…

2008.出租车的最大盈利

​​题目来源: leetcode题目,网址:2008. 出租车的最大盈利 - 力扣(LeetCode) 解题思路: 哈希表动态规划。根据终点区分不同的路线,然后进行动态规划。dp[i] 表示以当前节点为终点时的最大盈利。…

​LeetCode解法汇总1466. 重新规划路线

目录链接: 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目: https://github.com/September26/java-algorithms 原题链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 描述: n 座城市&…

在王者荣耀中脸探草丛的正确姿势是什么?

引言 Cocos中躲草丛效果的实现原理。 在游戏开发中,我们经常用透视或者半透明效果去表现模型被遮挡的效果。 本文将介绍一下如何在Cocos中实现王者荣耀中的躲草丛效果。 本文源工程在文末获取,小伙伴们自行前往。 躲草丛效果的实现原理 要在Cocos中…

Android Studio的笔记--String和byte[]

String和byte[]的相互转换,字节数组转换 String转换byte[]文本16进制字节数组 byte[]转换String文本16进制 其它 String转换byte[] 文本 将字符串(String)转换为字节(byte)的方法。默认使用的是UTF-8编码 StandardCh…

Qt设置应用程序字体

目的 由于微软雅黑字体具有版权效应, 导致实际项目中需要load其他字体, 比如说应用程序默认字体为思源黑体-Mdeium黑度。 通用做法在qrc中添加字体资源,SourceHanSansCN-Medium.ttf, 然后在main函数中动态加载字体文件。 如果后…

Linux本地部署1Panel服务器运维管理面板并实现公网访问

文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透3. 配置1Panel公网访问地址4. 公网远程访问1Panel管理界面5. 固定1Panel公网地址 前言 1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器,包括主机监控、…

剑指 Offer(第2版)面试题 20:表示数值的字符串

剑指 Offer(第2版)面试题 20:表示数值的字符串 剑指 Offer(第2版)面试题 20:表示数值的字符串解法1:模拟解法2:分段匹配解法 3:DFA 剑指 Offer(第2版&#xf…

linux进程通信

匿名管道 struct_file的两套资源 管道只能单向通信 特征 1.如果管道没有数据 读端在读 默认会直接阻塞正在读取的进程 2.写端写满 在写会阻塞 等待对方读取 管道设计 命名管道 实现管道通信 #pragma once #include<iostream> #include<string> #include<sys/…

windows MYSQL解决中文乱码问题

1.首先确保你已经把mysql配置了环境变量 2.打开window终端 3.输入mysql -u root -p 4.输入密码&#xff0c;就是安装的时候设置的root超级管理员权限密码 5.输入&#xff1a; SHOW VARIABLES LIKE ‘character%’; 出现上图&#xff0c;说明就会出现中文乱码问题。 6.该怎么办…

前端中的响应式布局与各个端适配

什么是响应式布局&#xff1f; 响应式布局指的是同一页面在不同屏幕尺寸下有不同的布局。在移动互联网高度发达的今天&#xff0c;我们在桌面浏览器上开发的网页已经无法满足在移动设备上查看的需求。传统的开发方式是PC端开发一套页面&#xff0c;手机端再开发一套页面。但是…

使用python脚本一个简单的搭建ansible集群

1.环境说明&#xff1a; 角色主机名ip地址控制主机server192.168.174.150受控主机/被管节点client1192.168.174.151受控主机/被管节点client2192.168.174.152 2.安装python和pip包 yum install -y epel-release yum install -y python python-pip 3.pip安装依赖库 pip in…

redis-学习笔记(hash)

Redis 自身已经是 键值对 结构了 Redis 自身的键值对就是通过 哈希 的方式来组织的 把 key 这一层组织完成后, 到了 value 这一层, 还可以用 哈希类型 来组织 (简单的说就是哈希里面套哈希 [数组里面套数组 -> 二维数组] ) [ field value ] hset key field value [ field va…

【Spring】Spring 微服务中的数据分区和分片

文章目录 前言数据分区简介分区的本质分区类型为什么分区很重要&#xff1f;分片简介了解分片分片的机制为什么选择分片&#xff1f;在 Spring 微服务中实现分区和分片用于分区的 Spring Data JPA挑战与最佳实践分区和分片时的注意事项选择正确的键重新分片和重新分区基础设施和…