讲讲 JVM 的内存结构(附上Demo讲解)

讲讲 JVM 的内存结构

  • 什么是 JVM 内存结构?
    • 线程私有
      • 程序计数器​
      • 虚拟机栈
      • 本地方法栈
    • 线程共享
      • 堆​
      • 方法区​
      • 注意
        • 永久代​
        • 元空间​
        • 运行时常量池​
        • 直接内存​
  • 代码详解

什么是 JVM 内存结构?

在这里插入图片描述

JVM内存结构分为5大区域,程序计数器、虚拟机栈、本地方法栈、堆、方法区。​

HotSpot在JDK1.8之前方法区就是永久代,永久代就是方法区。JDK1.8后删除了永久代,改为元空间,元空间在本地内存中。方法区就是元空间,元空间就是方法区。​

创建一个线程,JVM就会为其分配一个私有内存空间,其中包括PC、虚拟机栈和本地方法栈​

简单来说:

  • 堆:存放 new 出来的东西
  • 方法区:被虚拟机加载的类信息、常量、静态常量等。
  • 栈:存放局部变量
  • 程序计数器:记录指令
  • 本地方法栈:Native 方法

接着进行详细的介绍:

线程私有

程序计数器​

线程私有的,作为当前线程的行号指示器,用于记录当前虚拟机正在执行的线程指令地址。程序计数器主要有两个作用:​

  • 当前线程所执行的字节码的行号指示器,通过它实现代码的流程控制,如:顺序执行、选择、循环、异常处理。​
  • 多线程的情况下,程序计数器用于记录当前线程执行的位置,当线程被切换回来的时候能够知道它上次执行的位置。​

程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。​

虚拟机栈

Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。每一次函数调用都会有一个对应的栈帧被压入虚拟机栈,每一个函数调用结束后,都会有一个栈帧被弹出。​

局部变量表是用于存放方法参数和方法内的局部变量。​

每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,在方法调用过程中,会进行动态链接,将这个符号引用转化为直接引用。​

  • 部分符号引用在类加载阶段的时候就转化为直接引用,这种转化就是静态链接​
  • 部分符号引用在运行期间转化为直接引用,这种转化就是动态链接​

Java 虚拟机栈也是线程私有的,每个线程都有各自的 Java 虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。

Java 虚拟机栈会出现两种错误:StackOverFlowErrorOutOfMemoryError。​
可以通过 -Xss 参数来指定每个线程的虚拟机栈内存大小:java -Xss2M​

本地方法栈

虚拟机栈为虚拟机执行 Java 方法服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。Native 方法一般是用其它语言(C、C++等)编写的。​

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。​

线程共享

堆​

堆用于存放对象实例,是垃圾收集器管理的主要区域,因此也被称作GC堆。​

堆可以细分为:新生代(Eden空间(伊甸园)、From Survivor、To Survivor空间)和老年代。​

通过 -Xms 设定程序启动时占用内存大小,通过 -Xmx 设定程序运行期间最大可占用的内存大小。如果程序运行需要占用更多的内存,超出了这个设置值,就会抛出OutOfMemory异常。​

方法区​

方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。​

对方法区进行垃圾回收的主要目标是对常量池的回收和对类的卸载。​

注意

永久代​

方法区是 JVM 的规范,而永久代PermGen 是方法区的一种实现方式,并且只有 HotSpot 有永久代。对于其他类型的虚拟机,如 JRockit 没有永久代。由于方法区主要存储类的相关信息,所以对于动态生成类的场景比较容易出现永久代的内存溢出。​

元空间​

JDK 1.8 的时候,HotSpot的永久代被彻底移除了,使用元空间替代。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。

两者最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。

为什么要将永久代替换为元空间呢?​
永久代内存受限于 JVM 可用内存,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是相比永久代内存溢出的概率更小。​

运行时常量池​

运行时常量池是方法区的一部分,在类加载之后,会将编译器生成的各种字面量和符号引号放到运行时常量池。在运行期间动态生成的常量,如 String 类的 intern()方法,也会被放入运行时常量池。​

直接内存​

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

NIO 的 Buffer 提供了 DirectBuffer,可以直接访问系统物理内存,避免堆内内存到堆外内存的数据拷贝操作,提高效率。

DirectBuffer直接分配在物理内存中,并不占用堆空间,其可申请的最大内存受操作系统限制,不受最大堆内存的限制。​

直接内存的读写操作比堆内存快,可以提升程序I/O操作的性能。通常在I/O通信过程中,会存在堆内内存到堆外内存的数据拷贝操作,对于需要频繁进行内存间数据拷贝且生命周期较短的暂存数据,都建议存储到直接内存。

在这里插入图片描述

代码详解

以下是一个简单的 Java 类,我们将在注释中说明各个内存区域的作用:

public class MemoryStructureExample {// 静态变量,存放在方法区private static int staticVar = 42;public static void main(String[] args) {// 局部变量,存放在虚拟机栈int localVar = 10;// 创建一个对象,存放在堆中MemoryStructureExample obj = new MemoryStructureExample();// 调用方法,创建一个新的栈帧obj.doSomething(localVar);// 创建一个数组,存放在堆中int[] array = new int[5];// 常量,存放在方法区final String constantString = "Hello, world!";// 本地方法调用,使用本地方法栈System.out.println("Static variable: " + staticVar);System.out.println("Constant string: " + constantString);}// 方法,存放在方法区public void doSomething(int value) {// 局部变量,存放在虚拟机栈int result = value * 2;System.out.println("Result: " + result);}
}

在这段代码中,我们可以看到:

  • staticVar 是一个静态变量,存放在方法区。
  • localVar 是一个局部变量,存放在虚拟机栈。
  • obj 是一个对象,存放在堆中。
  • array 是一个数组,也存放在堆中。
  • constantString 是一个常量,存放在方法区。
  • doSomething 方法创建了一个新的栈帧,其中的局部变量存放在虚拟机栈。

总之,这些内存区域共同构成了 Java 虚拟机的内存结构,确保 Java 程序的正常运行和优化。如果你还有其他问题,欢迎继续提问!

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

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

相关文章

C# 泛型

泛型 泛型不是语法糖,而是由框架提供的一种便捷语法,首次出现在.NET 2.0中。 1. 泛型定义 泛型:是一种程序特性,定义时不对类型做出明确的规定,使用时规定且不能改变。一般应用:泛型集合、泛型方法、泛型…

机器学习——LR、‌GBDT、‌SVM、‌CNN、‌DNN、‌RNN、‌Word2Vec等模型的原理和应用

LR(逻辑回归) 原理: 逻辑回归模型(Logistic Regression, LR)是一种广泛应用于分类问题的统计方法,尤其适用于二分类问题。其核心思想是通过Sigmoid函数将线性回归模型的输出映射到(0,1)区间,从…

【AI前沿】深度学习:神经网络基础

文章目录 📑引言一、神经元和感知器1.1 神经元的基本概念1.2 感知器模型 二、多层感知器(MLP)2.1 MLP的基本结构2.2 激活函数的重要性2.3 激活函数2.4 激活函数的选择 三、小结 📑引言 深度学习是现代人工智能的核心技术之一&…

kotlin Flow 学习指南 (三)最终篇

目录 前言Flow生命周期StateFlow 替代LiveDataSharedFlow其他常见应用场景处理复杂、耗时逻辑存在依赖关系的接口请求组合多个接口的数据 Flow使用注意事项总结 前言 前面两篇文章,介绍了Flow是什么,如何使用,以及相关的操作符进阶&#xff…

如何挑选适合的需求池管理系统?10款优质工具分享

本文将分享10款优质需求池管理工具:PingCode、Worktile、Teambition、Epicor Kinetic、TAPD、SAP IBP、Logility、RELEX Solutions、JIRA、明道云。 在管理项目和产品需求时,正确的工具能够大幅提高效率与透明度。如何从众多需求池工具中选择最适合团队的…

第一节 SHELL脚本中的常用命令(2)

二,网络管理命令nmcli 1.查看网卡 # 或者先用ip addr或ip a等查看网卡 ip a s 网卡名 ifconfig 网卡名 nmcil device show 网卡名 nmcil device status nmcil connection show 网卡名2.设置网卡 a)当网卡未被设置过时 设置dncp网络工作模式 nmcil connection add con-name…

Rust编程-编写自动化测试

编写单元测试步骤: 1. 准备所需的数据 2. 调用需要测试的代码 3. 断言运行结果与我们所期望的一致 Rust的test元数据: #[cfg(test)]:是一个属性宏(attribute macro)。用于控制特定的代码段仅在测试环境中编译…

自定义类型:联合体

像结构体一样,联合体也是由一个或者多个成员组成,这些成员可以是不同的类型。 联合体类型的声明 编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。 输出结果: 联合体…

size_t 数据类型的好处

什么是size_t size_t 类型在不同的平台上对应不同的底层整数类型,具体取决于平台的指针大小。size_t 主要用于表示大小和长度,如数组的元素数量、缓冲区的大小等,它的设计目的是为了匹配指针的大小,以避免类型不匹配引起的错误。…

代码随想录算法训练营DAY58|101.孤岛的总面积、102.沉没孤岛、103. 水流问题、104.建造最大岛屿

忙。。。写了好久。。。。慢慢补吧。 101.孤岛的总面积 先把周边的岛屿变成水dfs def dfs(x, y, graph, s):if x<0 or x>len(graph) or y<0 or y>len(graph[0]) or graph[x][y]0:return sgraph[x][y]0s1s dfs(x1, y, graph, s)s dfs(x-1, y, graph, s)s dfs(…

【爬虫入门知识讲解:xpath】

3.3、xpath xpath在Python的爬虫学习中&#xff0c;起着举足轻重的地位&#xff0c;对比正则表达式 re两者可以完成同样的工作&#xff0c;实现的功能也差不多&#xff0c;但xpath明显比re具有优势&#xff0c;在网页分析上使re退居二线。 xpath 全称为XML Path Language 一种…

软考高级第四版备考--第16天(规划沟通管理)Plan Communication Management

定义&#xff1a;基于每个干系人或干系人群体的信息需求、可用的组织资产以及具体的项目的需求&#xff0c;为项目沟通活动制定恰当的方法和计划的过程。 作用&#xff1a; 及时向干系人提供相关信息&#xff1b;引导干系人有效参与项目&#xff1b;编制书面沟通计划&#xf…

【基于R语言群体遗传学】-16-中性检验Tajima‘s D及连锁不平衡 linkage disequilibrium (LD)

Tajimas D Test 已经开发了几种中性检验&#xff0c;用于识别模型假设的潜在偏差。在这里&#xff0c;我们将说明一种有影响力的中性检验&#xff0c;即Tajimas D&#xff08;Tajima 1989&#xff09;。Tajimas D通过比较数据集中的两个&#x1d703; 4N&#x1d707;估计值来…

vue项目中常见的一些preset及其关系

Babel的作用 Babel主要用途是用来做js代码转换的&#xff0c;将最新的js语法或者api转换成低版本浏览器可兼容执行的代码。 语法兼容是指一些浏览器新特性增加的js写法&#xff0c;例如箭头函数 ()>{}&#xff1b;低版本的浏览器无法识别这些&#xff0c;会导致一些语法解…

spark shuffle写操作——UnsafeShuffleWriter

PackedRecordPointer 使用long类型packedRecordPointer存储数据。 数据结构为&#xff1a;[24 bit partition number][13 bit memory page number][27 bit offset in page] LongArray LongArray不同于java中long数组。LongArray可以使用堆内内存也可以使用堆外内存。 Memor…

秋招突击——7/9——字节面经

文章目录 引言正文八股MySQL熟悉吗&#xff1f;讲一下MySQL索引的结构&#xff1f;追问&#xff1a;MySQL为什么要使用B树&#xff1f;在使用MySQL的时候&#xff0c;如何避免索引失效&#xff1f;讲一下MySQL的事物有哪几种特征&#xff1f;MySQL的原子性可以实现什么效果&…

GESP C++ 三级真题(2023年9月)T2 进制判断

进制判断 问题描述 N进制数指的是逢N进一的计数制。例如&#xff0c;人们日常生活中大多使用十进制计数&#xff0c; 而计算机底层则一般使用二进制。除此之外&#xff0c;八进制和十六进制在一些场合也是 常用的计数制(十六进制中&#xff0c;一般使用字母A至F表示十至十五…

【区块链+跨境服务】粤澳健康码跨境互认系统 | FISCO BCOS应用案例

2020 年突如其来的新冠肺炎疫情&#xff0c;让社会治理体系面临前所未见的考验&#xff0c;如何兼顾疫情防控与复工复产成为社会 各界共同努力的目标。区块链技术作为传递信任的新一代信息基础设施&#xff0c;善于在多方协同的场景中发挥所长&#xff0c;从 而为粤澳两地的疫情…

uniapp上传文件并获取上传进度

1. 上传普通文件 uni.chooseMessageFile({count: 1,success: (res) > {console.log(res)console.log("res123456", res.tempFiles[0].path)const uploadTask uni.uploadFile({url: http://localhost:8000/demo,filePath: res.tempFiles[0].path,name: file,form…

CSS关于居中的问题

文章目录 1. 行内和块级元素自身相对父控件居中1.1. 块级元素相对父控件居中1.2. 行内元素相对于父控件居中 2. 实现单行文字垂直居中3. 子绝父相实现子元素的水平垂直居中3.1. 方案一3.1.1. 示例 3.2. 方案二3.2.1. 示例 3.3. 方案三(推荐)3.3.1. 示例 3.4. 方案四(了解一下) …