Java 内存模型(JMM)
JMM,全称 Java Memory Model ,中⽂释义Java内存模型
1. 概述
对于 Java 程序员来说,在虚拟机⾃动内存管理机制下,不再需要像 C/C++ 程序开发程序员这 样为每⼀个 new 操作去写对应的 delete / free 操作,不容易出现内存泄漏和内存溢出问 题。正是因为 Java 程序把内存控制权利交给 JVM 虚拟机。⼀旦出现内存泄漏和溢出⽅⾯的问题, 如果不了解虚拟机是怎样使⽤内存的,那么排查错误将会是⼀个⾮常艰巨的任务。
2. 运行时数据区域划分
- JVM 虚拟机在执⾏ Java 程序的过程中会把它管理的内存划分成若⼲个不同的数据区域。
- JDK 1.8 之前分为:线程共享( Heap 堆区、 Method Area ⽅法区)、线程私有(虚拟 机栈、本地⽅法栈、程序计数器)
- JDK 1.8 以后分为:线程共享( Heap 堆区、 MetaSpace 元空间)、线程私有(虚拟机 栈、本地⽅法栈、程序计数器)



3. 程序计数器(Program Counter Register)
程序计数器是⼀块较⼩的内存空间,是当前线程所执⾏的字节码的⾏号指示器。
字节码解释器在解释执⾏字节码⽂件⼯作时,每当需要执⾏⼀条字节码指令时,就通过改变程序 计数器的值来完成。程序中的分⽀、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器 来完成。
程序执⾏过程中,会不断的切换当前执⾏线程,切换后,为了能让当前线程恢复到正确的执⾏位 置,每条线程都需要有⼀个独⽴的程序计数器,并且各线程之间计数器互不影响,独⽴存储。
程序计数器主要有两个作⽤:
- 字节码解释器通过改变程序计数器来依次读取指令,从⽽实现代码的流程控制,如:顺序执⾏、 选择、循环、异常处理。
- 在多线程的情况下,程序计数器⽤于记录当前线程执⾏的位置,从⽽当线程被切换回来的时候, 能够知道当前线程的运⾏位置。
- 程序计数器是唯⼀⼀个不会出现 OutOfMemoryError 的内存区域,它随着线程的创建⽽创建, 随着线程的结束⽽死亡。
4. Java 虚拟机栈(VM Stack)
与程序计数器⼀样, VM Stack 虚拟机栈也是线程私有的,它的⽣命周期和线程相同,⽤于描述 Java ⽅法执⾏时的内存模 型,每次⽅法调⽤的数据都是通过栈传递的。
JMM 内存区域可以粗略的区分为堆内存( Heap )和栈内存 ( Stack )。其中栈就是 VM St ack 虚拟机栈,或者说是虚拟机栈中局部变量表部分。 因为局部变量表主要存放了编译期可知的各 种基本数据类型变量值(boolean、byte、char、short、int、float、long、double)、对象引⽤ (reference 类型,它不同于对象本身,可能是⼀个指向对象起始地址的引⽤指针,也可能是指向⼀ 个代表对象的句柄或其他与此对象相关的位置)。

Java 虚拟机栈是由⼀个个栈帧组成,⽽每个栈帧中都拥有:局部变量表、操作数栈、动态链 接、⽅法出⼝信息。每⼀次⽅法调⽤都会有⼀个对应的栈帧被压⼊ VM Stack 虚拟机栈,每⼀个 ⽅法调⽤结束后,代表该⽅法的栈帧会从 VM Stack 虚拟机栈中弹出。

在活动线程中, 只有位于栈顶的帧才是有效的, 称为当前活动栈帧,代表正在执⾏的当前⽅ 法。在 JVM 执⾏引擎运⾏时, 所有指令都只能针对当前栈帧进⾏操作。虚拟机栈通过 pop 和 p ush 的⽅式,对每个⽅法对应的活动栈帧进⾏运算处理,⽅法正常执⾏结束,肯定会跳转到另⼀个 栈帧上。

Java ⽅法有两种返回⽅式: return 语句或抛出异常,不管哪种返回⽅式都会导致栈帧被弹出。
Java 虚拟机栈会出现两种错误: StackOverFlowError 和 OutOfMemoryError 。
- StackOverFlowError : 当线程请求栈的深度超过 JVM 虚拟机栈的最⼤深度的时候,就 抛出 StackOverFlowError 错误。
- OutOfMemoryError: JVM 的内存⼤⼩可以动态扩展, 如果虚拟机在动态扩展栈时⽆法申请 到⾜够的内存空间,则抛出 OutOfMemoryError 异常。
5. 本地方法栈(Native Method Stack)
本地方法栈用于虚拟机调用的 Native 方法
native 关键字修饰的本地⽅法被执⾏的时候,在本地⽅法栈中也会创建⼀个栈帧,⽤于存放 该 native 本地⽅法的局部变量表、操作数栈、动态链接、⽅法出⼝信息。⽅法执⾏完毕后,相应 的栈帧也会出栈并释放内存空间。也会出现 StackOverFlowError 和 OutOfMemoryError 两种错误。
6. 堆(Heap)
Heap 堆区,⽤于存放对象实例和数组的内存区域。
Heap 堆是 JVM 所管理的内存中最⼤的⼀块区域,被所有线程共享的⼀块内存区域。堆区中 存放对象实例,“⼏乎”所有的对象实例以及数组都在这⾥分配内存。
6.1. 新⽣代、⽼年代
Heap 堆是垃圾收集器 GC ( Garbage Collected )管理的主要区域,因此堆区也被称 作 GC 堆( Garbage Collected Heap )。从垃圾回收的⻆度,由于现在收集器基本都采⽤分 代垃圾收集算法,所以 JVM 中的堆区往往进⾏分代划分,例如:新⽣代 和 ⽼年代。⽬的是更好地 回收内存,或者更快地分配内存。

Heap 堆区中的新⽣代、⽼年代的空间分配⽐例,可以通过 java -XX:+PrintFlagsFinal -version 命令查看。
因为新⽣代是由 Eden + s0 + s1 组成的,所以按照上述默认⽐例,如果 Eden 区内存 ⼤⼩是 40M,那么两个 Survivor 区就是 5M,整个新⽣代区就是 50M,然后可以算出 Old 区内存 ⼤⼩是 100M,堆区总⼤⼩就是 150M。
6.2. 创建对象的内存分配
创建⼀个新对象,在堆中的分配内存。
⼤部分情况下,对象会在 Eden 区⽣成,当 Eden 区装填满的时候,会触发 Young Gar bage Collection ,即 YGC 垃圾回收的时候,在 Eden 区实现清除策略,没有被引⽤的对象 则直接回收。
依然存活的对象会被移送到 Survivor 区。 Survivor 区分为 s0 和 s1 两块内存区 域。每次 YGC 的时候,它们将存活的对象复制到未使⽤的 Survivor 空间( s0 或 s1 ), 然后将当前正在使⽤的空间完全清除,交换两块空间的使⽤状态。每次交换时,对象的年龄会 加 +1 。
如果 YGC 要移送的对象⼤于 Survivor 区容量的上限,则直接移交给⽼年代。⼀个对象 也不可能永远呆在新⽣代,在 JVM 中 ⼀个对象从新⽣代晋升到⽼年代的阈值默认值是 15 ,可 以在 Survivor 区交换 14 次之后,晋升⾄⽼年代。

堆区最容易出现的就是 OutOfMemoryError 错误,这种错误的表现形式会有以下两种:
- OutOfMemoryError: GC Overhead Limit Exceeded : 当 JVM 花太多时间执⾏垃 圾回收,并且只能回收很少的堆空间时,就会发⽣此错误。
- OutOfMemoryError: Java heap space :假如在创建新的对象时, 堆内存中的空间不⾜ 以存放新创建的对象, 就会引发此错误。
7. 元空间(Meta Space)
⽤于存放类信息、常量、静态变量、JIT即时编译器编译后的机器代码等数据等。例如: java.l ang.Object 类的元信息、 Integer.MAX_VALUE 常量等。
JDK1.6:
HotSpot JVM 使⽤ Method Area ⽅法区存储,也叫永久代(Permanent Generation)。
- ⽅法区和“永久代( Permanent Generation )”的区别:⽅法区是 JVM 的规范,⽽永久代( Permanent Generation )是 JVM 规范的⼀种实现,并且只有 HotSpot JVM 才有永久 代“ Permanent Generation ”,⽽对于其他类型的虚拟机,如 JRockit(Oracle)、J9 (IBM) 并没有;
- ⽅法区是⼀⽚连续的堆空间,当 JVM 加载的类信息容量超过了最⼤可分配空间,虚拟机会抛 出 OutOfMemoryError:PermGenspace 的 Error 。
- 永久代的GC是和⽼年代( old generation )捆绑在⼀起的,⽆论谁满了,都会触发永久代和 ⽼年代的垃圾收集。
- 可以通过 -XX:PermSize=N 设置 ⽅法区 (永久代) 初始空间, -XX:MaxPermSize=N 设 置⽅法区 (永久代) 最⼤空间,超过这个值将会抛出错误: java.lang.OutOfMemoryError: PermGen
JDK1.7:
将字符串常量池、静态变量转移到了堆区。
JDK1.8:正式移除永久代,采⽤ Meta Space 元空间代替
元空间的本质和永久代类似,都是对 JVM 规范中⽅法区的⼀种具体实现。不过元空间与永久代 之间最⼤的区别在于:元空间并不在虚拟机中,⽽是使⽤本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过运⾏参数来指定元空间的大小。
- 由于 PermGen 内存经常会溢出,容易抛出 java.lang.OutOfMemoryError: PermGen 错误;
- 移除 PermGen 可以促进 HotSpot JVM 与 JRockit VM 的融合,因为 JRock it 没有永久代;

8. 字符串常量池
String 的两种创建⽅式:
- 第⼀种⽅式是在常量池中获取字符串对象;
- 第⼆种⽅式是直接在堆内存空间创建⼀个新的字符串对象;

String 的 intern() ⽅法:

String 的拼接:


String s1 = new String("abc"); 这句代码创建了⼏个字符串对象?
创建 1 或 2 个字符串。如果常量池中已存在字符串常量“ abc ” ,则只会在堆空间创建⼀个字符串常 量“ abc ” 。如果常量池中没有字符串常量“ abc ” ,那么它将⾸先在池中创建,然后在堆空间中创建,因 此将创建总共 2 个字符串对象。
8 种基本类型的包装类和常量池
- Java 基本类型的包装类的⼤部分都实现了常量池技术,即 Byte,Short,Integer,Long,Character,Boolean;
- 前⾯ 4 种包装类默认创建了数值[-128,127] 的相应类型的缓存数据;
- Character 创建了数值在[0,127]范围的缓存数据;
- Boolean 直接返回 True Or False;
- 如果超出对应范围仍然会去创建新的对象
相关文章:
Java 内存模型(JMM)
JMM,全称 Java Memory Model ,中⽂释义Java内存模型 1. 概述 对于 Java 程序员来说,在虚拟机⾃动内存管理机制下,不再需要像 C/C 程序开发程序员这 样为每⼀个 new 操作去写对应的 delete / free 操作,不容易出现内存…...
用于安全研究的 Elastic Container Project
作者:来自 Elastic Andrew Pease•Colson Wilhoit•Derek Ditch 使用 Docker 启动 Elastic Stack 序言 Elastic Stack 是一个模块化数据分析生态系统。虽然这允许工程灵活性,但建立开发实例进行测试可能很麻烦。建立 Elastic Stack 的最简单方法是使用…...
STM8L101低功耗的理解
一.通过降低时钟频率来降低功耗: 规格书如图 1.code 从flash启动,主频率是16Mhz时,功耗测量2.51ma左右,对应程序如下: 2.code从flash启动,主频率是2Mhz时(上电默认值),功…...
Kotlin cancel CoroutineScope.launch的任务后仍运行
Kotlin cancel CoroutineScope.launch的任务后仍运行 import kotlinx.coroutines.*fun main() {runBlocking {val coroutineScope CoroutineScope(Dispatchers.IO)val job coroutineScope.launch {var i 0while (i < Int.MAX_VALUE) {iprintln(i)}}// 2ms 取消协程delay(…...
你是不是分不清哪些字体是商用,哪些非商用?快来看,免得莫名其妙负债。
前言 最近发现有好多小伙伴在做PPT的时候,都有一个很不好的习惯:没有调整好字体。 这里说的没有调整好字体的意思是:在一些公开发布的内容上使用一些可能造成侵权的字体。 字体侵权的后果相当严重。轻者可能面临法律纠纷,重者…...
新电脑工作流搭建记录-前端篇
vscode: url: Visual Studio Code - Code Editing. Redefined 插件:Chinese、git history、git graph、codelf、css peek、auto closed tad、auto rename tag、Quokka.js、Image preview Node 官网直接下载:下载 | Node.js node版本管理…...
XXL-JOB 漏洞大全
一、前言 在当今的数字化时代,任务调度平台对于企业级应用来说至关重要。它们负责自动化和协调各种时间敏感或周期性的任务,确保业务流程的顺畅运行。XXL-JOB作为一款流行的分布式任务调度平台,因其强大的功能和易用性,被广泛部署…...
使用 Visual Studio Code 配置 C/C++ 开发环境
Visual Studio Code(简称 VSCode)是一款非常流行的代码编辑器,提供了丰富的扩展和配置支持,使其成为进行 C/C 开发的一款理想工具。本文将详细介绍如何在 VSCode 中配置 C/C 开发环境,涵盖安装必要的工具和插件、编写简…...
STM32与ESP8266的使用
串口透传 “透传”通常指的是数据的透明传输,意思是在不对数据进行任何处理或修改的情况下,将数据从一个接口转发到另一个接口。值得注意的是要避免串口之间无限制的透明,可以采用互斥锁的方式进行限制使用方法 对USART1和USART3(用他俩举例…...
【计算机网络】数据链路层深度解析
概述三个重要问题封装成帧差错检测可靠传输 使用广播信道的数据链路层数据链路层的互连设备 媒体接入MAC地址集线器与交换机区别以太网交换机生成树协议STP 概述 链路就是从一个结点到相邻结点的一段物理线路,而中间没有任何其他的交换结点。数据链路是指把实现通信…...
【基于轻量型架构的WEB开发】【章节作业】
作业1 mybatis核心对象、配置文件和映射文件 一. 单选题(共10题,50分) 1. (单选题)以下关于<select>元素及其属性说法错误的是()。 A. <select>元素用来映射查询语句,它可以帮助我们从数据库中读取出数据,并组装数据给业务开发…...
一张图解析FastAdmin中的表格列表(bootstrap-table)的功能(备份)
功能描述 请根据图片上的数字索引查看对应功能说明。 1.菜单名称和描述 默认生成的CRUD是没有菜单名称和描述显示的,如果需要显示则可以修改权限管理->菜单规则,给对应菜单的添加上备注信息后即可显示,支持HTML 2.TAB过滤选项卡 在一键…...
【数据结构】假设二叉树采用二叉链表存储,编写一棵二又树中序遍历的非递归算法。
编程题: 假设二叉树采用二叉链表存储,编写一棵二又树中序遍历的非递归算法。 分析: 算法描述: 非递归中序遍历二叉树的算法使用栈来辅助实现。首先,从根节点开始,沿着左子树不断向下, 将每个节点压入栈中。当到达最左端节点后,开始出栈并访问节点,接着转向右子树,重…...
李宏毅结构化学习 02
文章目录 一、上篇博文复习二、Separable Case三、Non-separable Case四、Considering Errors五、Regularization六、Structured SVM七、Cutting Plane Algorithm for Structured SVM八、Multi-class and binary SVM九、Beyond Structured SVM 一、上篇博文复习 图中x表示输入的…...
Android AlertDialog圆角背景不生效的问题
一行解决: window?.setBackgroundDrawableResource(android.R.color.transparent) 原文件: /*** Created by Xinghai.Zhao* 自定义选择弹框*/ SuppressLint("InflateParams", "MissingInflatedId") class CustomDialog(context: Context?) : AlertDia…...
探讨基于AI技术的相亲交友系统设计与实现
摘要 随着人工智能技术的发展,相亲交友领域也开始引入AI技术来改善用户体验,提高匹配成功率。本文探讨了如何利用AI技术设计并实现一个智能化的相亲交友系统,该系统能够根据用户的行为数据和个人偏好,自动推荐合适的潜在伴侣。通…...
(2024.9.20)Endnote插入的参考文献字号太大怎么办?
1、序言 常常写论文的人都知道,插入参考文献时,格式调整到让人头大。Endnote的使用大大方便了我们的同时,也意味着我们要学习软件的使用方法。最近重新安装了一下Endnote,插入的文献字体大小就像抽风了一样。在还没有写完文章之前…...
DataGrip在Windows和MacOS平台上的快捷键
0. 背景信息 No.说明1测试DataGrip版本号 : 2024.2.2 1. Windows下快捷键 2. MacOS下快捷键...
CSS---序号使用css设置,counter-reset、counter-increment、content配合实现备注文案的序号展示
直接上代码,全代码copy即可使用! <template><div class"reminder"><span class"Bold_12_body" style"line-height: 8vw">温馨提示:</span><br /><div class"rule-container"…...
Liquor 表达式引擎基本使用
引入依赖 <dependency><groupId>org.noear</groupId><artifactId>liquor-eval</artifactId><version>1.2.7</version> </dependency>liquor 表达式引擎(ExpressionEvaluator)支持 java 所有的类型、及…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
