JVM(Java Virtual Machine)内存模型篇

news2024/12/27 12:07:19

前言

本文是JVM系列的内存模型篇,参考资料为《深入理解Java虚拟机》,本文章将会以HotSpot 虚拟机为介绍基础。

1.JVM简单介绍

Java Virtual Machine是运行Java程序的基础,JVM基于C、C++实现,JVM有很多种类,但是这些虚拟机都必须按照《Java虚拟机规范》来进行实现。目前JDK使用的是HotSpot虚拟机。

2.JVM内存模型

根据《Java虚拟机规范》的规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域

  • 程序计数器
  • Java虚拟机栈
  • 本地方法栈
  • 方法区

分布如下图:
在这里插入图片描述

3.程序计数器

程序计数器是Java中占用内存比较少的一个区域,他的作用是记录当前线程所执行的字节码的行号指令,通俗的理解就是代码执行到哪里了

我们很容易思考到,在多线程中,是发生线程切换这种情况的,那么一个线程被切换后,它的状态就需要被记录到上下文中,方便线程能正确执行到原来的位置,那么为了记录这个位置,就需要程序计数器来进行实现

为了线程切换后能恢复到正确的执行位置,每个线程都需要一个独立程序计数器,各个线程之间计数器互不影响,独立存储。因此它也是“线程私有”的内存

这片区域也是唯一一个在《Java虚拟机规范》中没有任何OutOfMemoryError情况的区域。

4.Java虚拟机栈

Java虚拟机栈,以“栈”命名的,在内存模型中,基本都是用来处理方法的,所以Java虚拟机栈是用来处理Java语言实现的方法的。同理的,这个栈也是线程私有的。他的生命周期与线程生命周期一样长。

一个线程在调用方法的时候,会在虚拟机栈中,创建一个栈帧,这个栈帧会存放局部变量表、操作数栈、动态连接、方法出口等信息。
在这里插入图片描述
栈帧包含以下内容:

  • 局部变量表: 栈帧用于存储方法的局部变量,表中存放了编译期可知的基本数据类型和对象引用(对象引用指针或者句柄)。这些局部变量在方法调用时分配内存空间,并在方法调用结束后被释放。

  • 操作数栈: 栈帧还包含一个操作数栈,用于存储方法执行时的操作数。当方法需要进行计算或操作时,操作数会被入栈或出栈。

  • 动态链接: 栈帧包含指向运行时常量池中当前方法引用的指针,用于在方法中访问其他类或方法。

  • 方法出口: 当方法调用完成后,程序需要返回到方法调用的地方继续执行。栈帧包含方法返回地址,用于记录返回的位置。

额外提一嘴的是当Java虚拟机栈的深度被方法调用填满的时候,就会出现StackOverFlowError;如果栈的大小动态扩展到没办法扩展的时候,会报OOM(OutOfMemoryError)的错误。

5.本地方法栈

这个栈和Java虚拟机栈是一样的功能,但是作用的对象不一样,Java虚拟机栈对应的是Java方法,而本地方法栈对应的是被Native标志的方法,这类方法一般都是C、C++代码。其他东西基本和Java虚拟机栈一致。

6.方法区

方法区与Java堆一样,是各个线程共享的内存区域,这块区域是用来存储已经被加载的类元信息,这些信息包含:类型信息、常量、静态变量、即使编译后的代码缓存等信息。

6.1永久代与元空间

早在JDK1.8以前,方法区使用的永久代的实现方式,而在1.8后才正式确定使用元空间。那么二者实现上有什么区别呢????

最大的区别就是前者是使用的虚拟机内存,后者使用了直接内存,也就是说永久代的内存大小受JVM限制,而元空间内存大小受真实机子内存大小限制,明显后者内存大小更大,前者更容易OOM。

在方法区使用元空间后,字符串常量池也从方法区移动到了堆内存中。

6.2运行时常量池

提到方法区,就不得不提到一个叫运行时常量池的东西,它也是方法区的一部分。

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

7.堆

堆内存是整个虚拟机中最大的一块,这块区域是被线程共享的,这块对象就是用来在程序执行时,大部分对象存放的地方(还有极小一部分可能会发生逃逸分析,在栈上创建和销毁)。

堆这块区域,也是最容易发生OOM的地方,原因可想而知,公共的地方,大家都来这里放东西,时间一长,没有空间也很正常,所以这块区域也是发生GC(Garbage Collected)频率最高的一个场所。(具体GC流程,下篇文章会详细介绍)

7.1 对象创建

堆中的对象(普通对象)创建过程也是比较讲究的,下面我们带着问题,一步一步理解这个过程

首先,如何创建?

很简单的,new关键字

那么问题又来了,对象创建依赖的信息从哪里来?

当Java遇到一条字节码new指令的时候,首先将去检测这个指令能否在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那么必须先执行相应的类加载(双亲委派模型)。

对象依赖信息得到后,内存大小该如何划分分配?

当一个类被加载之后,相应的对象创建所需的内存大小也就能被确定了。那么要在堆中创建对象,就需要划分空间,JVM中有两种划分空间的方式,分别是“指针碰撞”和“空闲列表”

  • 指针碰撞
    假设Java堆中内存分配绝对规整,使用过的和未使用的分成两边,只需要在边界设置指针,这个指针只需要挪动和对象大小一样的距离即可,这种就是指针碰撞
  • 空闲列表
    假如Java堆内存并不规整,使用过的和未使用的都混在一起,这种情况,要分配内存就只能维护一个列表,这个列表记录了哪些内存可以使用,分配内存就需要在表中查找到足够到的区间进行分配即可,这就是空闲列表

并发下,对象创建的内存分配安全如何得到保证?

为我们所知的,堆内存是一个线程共享的,这就意味着,我们堆在划分内存大小的时候,可能会出现线程安全问题。可能出现线程1在给A分配大小的时候,还没来得及修改指针,但是线程2在创建B时,使用了这个指针,就导致了内存数据被改写了。解决这个问题有两个方式

  • 加锁同步
    实际实现中虚拟机是采用CAS+失败重试的方式保证更新操作的原子性
  • TLAB(Thread Local Allocation Buffer,本地缓冲区),也和ThreadLocal一样,给每个线程各自划分好区域,线程要创建对象,就在这个区域内创建就行,如果TLAB使用完了才需要进行同步锁定分配对象。如果JVM要使用TLAB,可以通过-XX:+/-UseTLAB参数来设定

实际上,内存分配成功之后,虚拟机还会对分配到的内存空间(不包括对象头)进行初始化工作,零值处理。这步操作是为了保证对象实例字段在Java代码中可以不赋值就能直接使用。

经历以上步骤,对象创建后,对象还需要设置什么?

需要设置“对象属于哪个类的实例”、“类的元数据信息“”、“对象hash码(实际调用Object::hashCode才会生成)”、“GC分代年龄”,这些信息都被描述在对象头中

最终

在上面工作都完成后,看似一个对象已经被创建了,但实际上,整个生命过程还差一步,即初始化,构造函数中的初始化工作还没有被真正执行,也就是 < init > ()方法,所以值都是默认为零值的,所以当构造函数执行完成后,一个对象就被完成创建了。

7.2 对象的内存布局

在了解一个对象的创建过程后,我们来看看,一个对象内部布局是如何的,直接看下图:
在这里插入图片描述
对象头:这部分包含了两部分信息

  • 第一部分:HashCode、GC分代年龄、锁状态标记、线程持有的锁、偏向锁ID、偏向锁时间戳等信息等,这部分信息官方称之为:Mark Word,这部分数据在32位和64位虚拟机(未开启指针压缩)中分别占用32bit和64bit。
  • 第二部分:类型指针,即对象指向它的类型元数据的指针,Java虚拟机通过这个指针来确定这个对象是哪个类的实例。如果是数组对象,对象头中还会记录数组长度,如果不是则无记录。

实例数据:这部分数据是对象真正存储的有效信息

对齐填充:这部分的内容不是必然存在的,也没有特殊含义,这部分的主要作用就是保证这个对象大小是8字节的整数倍,差多少,尽可能补多少。

JVM执行流程

在这里插入图片描述

  • 代码编译:Java源代码通过Java编译器(javac)编译成字节码文件(.class文件)。

  • 类加载:JVM的类加载器将字节码文件加载到内存中,并进行校验、准备、解析等处理。

  • 内存分配:JVM为加载的类分配内存,包括方法区、堆、栈等。

  • 初始化:JVM对类进行初始化,包括静态变量的赋值、静态代码块的执行等。

  • 执行:JVM开始执行字节码指令,逐行读取字节码文件并执行。这个执行过程交给执行引擎将字节码翻译成CPU指令交给操作系统去执行

END
以上是本文全部内容,希望对你有所帮助

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

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

相关文章

idea 里 没有svn选项的处理办法

总结一下没有svn选项的几种情况&#xff1a; 情况1&#xff1a;IntelliJ IDEA打开带SVN信息的项目不显示SVN信息&#xff0c;项目右键SVN以及图标还有Changes都不显示解决方法 在VCS菜单中有个开关&#xff0c;叫Enabled Version Control Integration&#xff0c;在打开的窗口…

Mycat2 分布式数据库中间件

一.安装部署 Mycat2目前还不支持直接获取Docker镜像&#xff0c;需要自己通过Dockerfile打包镜像&#xff0c;其实这也是为了开发者考虑&#xff0c;比如一些个性化功能&#xff0c;如自定义分片等 Dockerfile FROM docker.io/adoptopenjdk/openjdk8:latestENV AUTO_RUN_DIR…

【软考-中级】系统集成项目管理工程师 【17 信息系统安全管理】

持续更新。。。。。。。。。。。。。。。 【第十七章】信息系统安全管理 (选择2分 17.1 信息安全管理17.1.1 信息安全含义和目标 17.2 信息系统安全17.2.1信息系统安全概念17.2.2信息系统安全属性17.2.3信息系统安全管理体系 17.3 物理安全管理17.3.1计算机机房与设施安全17.3.…

Postman入门基础 —— 接口测试流程!

一、编写接口测试计划 接口测试计划和功能测试计划目标一致&#xff0c;都是为了确认需求、确定测试环境、确定测试方法&#xff0c;为设计测试用例做准备&#xff0c;初步制定接口测试进度方案。一般来说&#xff0c;接口测试计划包括概述、测试资源、测试功能、测试重点、测…

【C++】用constexpr,constinit,consteval让程序跑的快一点

从C11加入constexpr关键字开始&#xff0c;到C20又加入了consteval &#xff0c;constinit &#xff0c;有3个const打头的关键字 虽然是以const开头的&#xff0c;不过这3个关键字主要是指示在编译时候的动作&#xff0c;它们都是在编译时就已经被编译程序处理&#xff0c;并非…

Python使用openpyxl读取excel图片

使用openpyxl读取excel中图片&#xff0c;并保存到本地. 需要的包。 from openpyxl import load_workbook from PIL import Image import cv2 import numpy as np具体实现 先把openpyxl读取的图片转换为Image对象&#xff0c;再将Image对象转换为numpy array&#xff0c;num…

【无标题】Docker入门到拿捏,图文码并茂

0. 项目部署的问题 大型项目组件较多,运行环境也较为复杂&#xff0c;部署时会碰到- -些问题: 依赖关系复杂&#xff0c;容易出现兼容性问题开发、测试、生产环境有差异 1. 认识Docker Docker如何解决依赖的兼容问题的? 将应用的Libs (函数库)、Deps (依赖)、配置与应用一…

V8引擎编译原理(VIP课程)

什么是V8 V8是谷歌的开源高性能JavaScript和WebAssembly引擎&#xff0c;用C编写。它被用于Chrome和Node.js等。它实现ECMAScript和WebAssembly&#xff0c;并在Windows 7或更高版本、macOS 10.12以及使用x64、IA-32、ARM或MIPS处理器的Linux系统上运行。V8可以独立运行&#…

KMP substring search 算法 案例分析

一、理解KMP算法如何运用后缀和前缀的信息 文本串text:abcxabcdabxabcdabcdabcy模式串pattern:abcdabcy 当发现不匹配的点&#xff0c;我们的目标不是在这个串中进行回溯操作。因此我们要检查的是 d 的前面的子串&#xff08;abc&#xff09;&#xff0c;在这个子串&#xff08…

【Qt-19】按Q退出应用程序

如何将Qt窗口应用程序改成控制台程序呢&#xff1f; 下面进入正文&#xff0c;如何控制控制台程序退出呢&#xff1f; 这里采用线程方式&#xff0c;通过单独线程监视用户输入来执行是否退出程序。 监视线程头文件thread.h #include <QThread> #include "TDRServe…

c++_learning-基础部分

文章目录 基础认识&#xff1a;语言特性&#xff08;面向对象编程&#xff09;&#xff1a;c的类&#xff08;相当于c中的结构体&#xff09;&#xff1a;三大特性&#xff1a;c包含四种编程范式&#xff1a;优缺点&#xff1a; c程序编译的过程&#xff1a;预处理->编译&am…

matlab中绘制 维诺图(Voronoi Diagram)

1.专业术语&#xff08;相关概念&#xff09;&#xff1a; 基点Site&#xff1a;具有一些几何意义的点 细胞Cell&#xff1a;这个Cell中的任何一个点到Cell中基点中的距离都是最近的&#xff0c;离其他Site比离内部Site的距离都要远。 Cell的划分&#xff1a;基点Site与其它的…

气膜式仓库:灵活创新,助力企业储存与物流升级

气膜式大空间仓库的建设不受地面条件限制&#xff0c;为企业提供了极大的便利。合理的仓储系统不仅是企业和厂商提高货品流动速度、确保生产、储运、配送顺利进行的关键&#xff0c;也是现代物流发展的需要。传统建筑在使用中存在一些不足&#xff0c;因此&#xff0c;我们需要…

科技新宠!拓世AI智能直播一体机揭秘,颠覆教学模式!

数字时代的铺展下&#xff0c;短视频和直播电商行业呈现出爆发式的增长&#xff0c;这种趋势正在日益融入人们的日常生活中&#xff0c;让短视频带货和直播带货逐渐成为一种独具中国特色的现象。与此同时&#xff0c;市场对专业人才的渴求也日渐加剧。国家以及相关地方政府纷纷…

Generative AI 新世界 | 大模型参数高效微调和量化原理概述

在上期文章&#xff0c;我们对比了在 Amazon SageMaker 上部署大模型的两种不同的部署方式。本期文章&#xff0c;我们将探讨两个目前大语言模型领域的开发者们都关注的两个热门话题&#xff1a;大型语言模型&#xff08;LLM&#xff09;的高效微调和量化。 微调大型语言模型允…

微信小程序调起微信支付

微信支付开发文档&#xff1a;wx.requestPayment(Object object) | 微信开放文档 一、先有一个提交订单页面 &#xff08;1&#xff09;wxml <view class"btn" bindtap"addOrder">{{btnText}}</view> 二、接入支付流程 &#xff08;1&…

element-ui 图片压缩上传

picture.js export const compressImgNew (file) > {return new Promise(resolve > {const reader new FileReader()const image new Image()image.onload (imageEvent) > {const canvas document.createElement(canvas) // 创建画布const context canvas.getCo…

工控机连接Profinet转Modbus RTU网关与水泵变频器Modbus通讯配置案例

Profinet转Modbus RTU网关是一个具有高性能的通信设备&#xff0c;它能够将工控机上的Profinet协议转换成水泵变频器可识别的Modbus RTU协议&#xff0c;实现二者之间的通信。通过这种方式&#xff0c;工控机可以直接控制水泵变频器的运行状态&#xff0c;改变其工作频率&#…

获取钉钉机器人的token及secret

一、下载安装 app不能自定义机器人&#xff0c;要客户端才行 二、进入组织/团队 三、创建群聊 1、发起群聊 2、创建内存群 群聊是内部的才行&#xff0c;只有内部的才支持自定义机器人 3、选中联系人 4、进入群设置 四、创建自定义机器人 1&#xff09; 进入机器人页面 2&…

工程云平台源码 建筑工地劳务实名制、危大工程监管平台源码

智慧工地的核心是数字化&#xff0c;它通过传感器、监控设备、智能终端等技术手段&#xff0c;实现对工地各个环节的实时数据采集和传输&#xff0c;如环境温度、湿度、噪音等数据信息&#xff0c;将数据汇集到云端进行处理和分析&#xff0c;生成各种报表、图表和预警信息&…