JVM(Java Virtual Machine)内存模型篇
前言
本文是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指令交给操作系统去执行
-
…
相关文章:
JVM(Java Virtual Machine)内存模型篇
前言 本文是JVM系列的内存模型篇,参考资料为《深入理解Java虚拟机》,本文章将会以HotSpot 虚拟机为介绍基础。 1.JVM简单介绍 Java Virtual Machine是运行Java程序的基础,JVM基于C、C实现,JVM有很多种类,但是这些虚…...
对地址解析协议ARP进一步探讨
之前在讨论MAC地址和IP地址时,顺便对ARP协议做了初步的总结 (计网第三章(数据链路层)(四)(MAC地址和IP地址、ARP协议、集线器和交换机)),但是当时对ARP请求的…...
java:java.util.StringTokenizer实现字符串切割
java:java.util.StringTokenizer实现字符串切割 1 前言 java.util工具包提供了字符串切割的工具类StringTokenizer,Spring等常见框架的字符串工具类(如Spring的StringUtils),常见此类使用。 例如Spring的StringUtil…...
IPV6 ND协议--源码解析【根源分析】
ND协议介绍 ND介绍请阅读上一篇文章:IPv6知识 - ND协议【一文通透】11.NDP协议分析与实践_router solicitation报文中不携带source link-layer address-CSDN博客 ND协议定义了5种ICMPv6报文类型,如下表所示: NS/NA报文主要用于地址解析RS/…...
Python学习笔记——存储容器
食用说明:本笔记适用于有一定编程基础的伙伴们。希望有助于各位! 列表 列表类似数组,其中可以包含不同类型的元素,写法如下: list1 [Google, Runoob, 1997, 2000] list2 [1, 2, 3, 4, 5 ] list3 ["a", …...
Android DI框架-Hilt
到底该如何理解<依赖注入> 模版代码:食之无味,弃之可惜 public class MainActivity extends Activity {Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);TextView mTextView(TextView) findVi…...
基于寄生捕食优化的BP神经网络(分类应用) - 附代码
基于寄生捕食优化的BP神经网络(分类应用) - 附代码 文章目录 基于寄生捕食优化的BP神经网络(分类应用) - 附代码1.鸢尾花iris数据介绍2.数据集整理3.寄生捕食优化BP神经网络3.1 BP神经网络参数设置3.2 寄生捕食算法应用 4.测试结果…...
【Java常见的几种设计模式】
Java常见的几种设计模式 1. 单例模式(Singleton Pattern)2. 工厂模式(Factory pattern)3. 抽象工厂模式(Abstract Factory Pattern)4. 建造者模式(Builder Pattern)5. 原型模式&…...
jupyter崩溃进不去,报错module ‘mistune‘ has no attribute ‘BlockGrammar‘
是python包引起的问题 [E 2023-10-14 08:40:25.414 ServerApp] Uncaught exception GET /api/nbconvert?1697244025327 (127.0.0.1) HTTPServerRequest(protocol‘http’, host‘localhost:8090’, method‘GET’, uri‘/api/nbconvert?1697244025327’, version‘HTTP/1.1’…...
windows terminal鼠标右键打开
如果在官网上下载的是zip文件的 需要在注册表修改鼠标右键才能出来 注册表修改如下: 1.先windowsR,在命令框中输入regedit 打开注册表 2.在路径’计算机\HKEY_CLASSES_ROOT\directory\background\shell’下新建一个wt,wt下新建commond 这里…...
HTML5播放 M3U8的hls流地址
在HTML5页面上播放M3U8的hls流地址 <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title>视频播放</title> <script src"https://cdn.jsdelivr.net/npm/hls.jslatest"></script> &…...
leetcode:101.对称二叉树
借用二叉树是否相同的代码改动左右孩子相等对应关系,即为是否对称。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/bool isSameTree(struct TreeNode* p, struct Tr…...
UI自动化的适用场景,怎么做?
经常有人会问,什么样的项目才适合进行UI自动化测试呢?UI自动化测试相当于模拟手工测试,通过程序去操作页面上的控件。而在实际测试过程中,经常会遇到无法找到控件,或者因控件定义变更而带来的维护成本等问题。 哪些场…...
SpringFramewrok (1)
1、框架的概念与理解 在现实生活中,框架可以比喻为我们搭建房子的框架。 在框架的基础上,我们可以专注于我们自己的工作,而不用在意这些底层工作如何实现。 框架的优点包括以下几点: 1. 提高开发效率:框架提供了许多…...
电商独立站小程序开发方案
随着移动互联网的迅速发展,电商行业也逐渐向小程序平台转移。开发一款电商小程序对于拓展销售渠道、提高用户体验、增加用户忠诚度等方面都有着重要的意义。本文将围绕电商小程序的开发背景、需求分析、技术选型、开发流程、风险控制、商业模式和市场前景等方面进行…...
数据库安全运维是什么意思?数据库安全运维系统用哪家好?
我们大家都直到数据在某些情况下容易丢失或被破坏,攻击者可能通过对数据库进行破坏或勒索等手段获取利益。所以保障数据库安全至关重要。今天我们就来聊聊数据库安全运维是什么意思?数据库安全运维系统用哪家好? 数据库安全运维是什么意思&…...
小程序的console中出现:。。。不在以下 request 合法域名列表中,请参考文档:。。。的报错解决
报错效果: 其实这个报错不代表自己的代码有问题 但是本强迫症研究了一下,按照以下方法关掉就不会显示这个报错了。 点微信开发者工具中的右上角的详情。点本地设置。勾选不校验。。。HTTPS证书。 即可关闭该报错:...
计算机网络基础(三):IPv4编址方式、子网划分、IPv4通信的建立与验证及ICMP协议
**IPv4地址是一个32位长的二进制数。**而这个32位二进制数又通常会表示为4个用点隔开的十进制数。那么,这个32位二进制数要如何通过4个十进制数表示出来呢? 我们在配置IPv4地址时,同时配置的“掩码”又有何用途? 1.IPv4编址方式…...
Error: GlobalConfigUtils setMetaData Fail Cause:java.lang.NullPointerException
文章目录 1、在开发中会出现这样的错误。2、其次,再看其他错误: 1、在开发中会出现这样的错误。 完整错误:Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Error: GlobalConfigUtils setMetaData Fail ! Cause…...
OpenHarmony 应用全局的 UI 状态存储:AppStorage
AppStorage 是应用全局的 UI 状态存储,是和应用的进程绑定的,由 UI 框架在应用程序启动时创建,为应用程序 UI 状态属性提供中央存储。 和 AppStorage 不同的是,LocalStorage 是页面级的,通常应用于页面内的数据共享。而…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
