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 所有的类型、及…...
身份证OCR识别接口接入实战:Python/Java/PHP/C#四语言代码示例与踩坑指南
#身份证OCR, #OCR接口, #API接入, #Python示例, #Java示例, #PHP示例, #踩坑指南, #石榴智能, #实名认证, #图片识别 身份证OCR识别接口接入实战:Python/Java/PHP/C#四语言代码示例与踩坑指南 作者:石榴智能技术团队 一、前言 身份证OCR识别已经不是什…...
Obsidian PDF++:如何在Obsidian中实现PDF与笔记的无缝双向链接?
Obsidian PDF:如何在Obsidian中实现PDF与笔记的无缝双向链接? 【免费下载链接】obsidian-pdf-plus PDF: the most Obsidian-native PDF annotation & viewing tool ever. Comes with optional Vim keybindings. 项目地址: https://gitcode.com/gh_…...
双系统Ubuntu磁盘告急?别重装!用GParted无损扩容保姆级教程(附U盘启动盘制作)
双系统Ubuntu磁盘告急?别重装!用GParted无损扩容保姆级教程(附U盘启动盘制作)当你在Windows和Ubuntu双系统环境下工作时,是否遇到过这样的窘境:当初安装时给Ubuntu分配的空间捉襟见肘,而Windows…...
【紧急预警】Lindy衰减临界点已提前至第8.3个月!2024最新《营销自动化寿命健康度白皮书》限时开放前500份
更多请点击: https://kaifayun.com 第一章:Lindy衰减临界点的理论重构与实证突破 Lindy效应传统上描述“越老越长寿”的非线性生存规律,但其在现代软件系统、开源生态与协议层技术栈中的适用边界正遭遇结构性挑战。本文首次将Lindy模型从静…...
Unity Visual Scripting不是拖拽玩具:中阶开发者的编程范式重构指南
1. 为什么Unity官方Visual Scripting不是“拖拽完就能跑”的玩具,而是一套需要重新理解的编程范式很多人第一次点开Unity的Visual Scripting(VS)面板时,看到那些五颜六色的节点和丝滑的连线,下意识觉得:“这…...
LeagueAkari:英雄联盟终极自动化助手革命性指南
LeagueAkari:英雄联盟终极自动化助手革命性指南 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 你是否在英雄联盟游戏中反复经历这…...
DeepSeek代码风格检查避坑指南(内部审计报告首次披露:37个被忽略的合规红线)
更多请点击: https://intelliparadigm.com 第一章:DeepSeek代码风格检查的合规性本质与审计背景 DeepSeek代码风格检查并非单纯的技术偏好约束,而是嵌入研发治理链条中的合规性控制节点。其本质是将编程实践与组织级安全策略、行业监管要求&…...
AutoPentest:面向红队的渗透测试决策引擎架构解析
1. 这不是又一个“自动化扫描器”,而是一套能替你做决策的渗透测试工作流引擎AutoPentest这个名字,第一眼容易让人联想到Nmap加个for循环、或者Burp Suite里点几下Intruder——但实际用过的人很快会意识到:它根本不在同一个维度上。我第一次在…...
Hitboxer:终极SOCD按键重映射解决方案,彻底解决游戏按键冲突问题
Hitboxer:终极SOCD按键重映射解决方案,彻底解决游戏按键冲突问题 【免费下载链接】socd Key remapper for epic gamers 项目地址: https://gitcode.com/gh_mirrors/so/socd 在激烈的游戏对战中,你是否曾因同时按下左右方向键而导致角色…...
3分钟掌握抖音视频批量下载:解放双手的素材收集革命
3分钟掌握抖音视频批量下载:解放双手的素材收集革命 【免费下载链接】douyinhelper 抖音批量下载助手 项目地址: https://gitcode.com/gh_mirrors/do/douyinhelper 还在为一个个手动保存抖音视频而烦恼吗?想要高效收集创作者素材却苦于没有合适的…...
