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 所有的类型、及…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...