JVM 虚拟机 ----> Java 内存模型(JMM)
文章目录
- Java 内存模型(JMM)
- 一、运行时数据区域划分
- 二、程序计数器(Program Counter Register)
- 计数器的作用
- 三、Java 虚拟机栈(VM Stack)
- 四、本地方法栈(Native Method Stack)
- 五、堆(Heap)
- 1、概述
- 2、新生代、老年代
- 3、创建对象的内存分配
- 六、元空间(Meta Space)
- 1、作用
- 2、发展历程
- (1)JDK 1.6
- (2)JDK 1.7
- (3)JDK 1.8
- 七、字符串常量池
- 1、字符串的两种创建方式
- 2、intern() 方法
- 3、String 的拼接
Java 内存模型(JMM)
JMM ,全称 Java Memory Model ,中文释义 Java 内存模型
一、运行时数据区域划分
- JVM 虚拟机在执行 Java 程序过程中会把它管理的内存划分成若干个不同的数据区域‘
JDK 1.8
之前分为:线程共享(Heap
堆区、Method Area
方法区)、线程私有(虚拟机栈、本地方法栈、程序计数器)JDK 1.8
以后分为:线程共享(Heap
堆区、MetaSpace
元空间)、线程私有(虚拟机栈、本地方法栈、程序计数器)
二、程序计数器(Program Counter Register)
程序计数器是一块较小的内存空间,是当前线程所执行的字节码的行号指示器
- 字节码解释器在解释执行字节码文件工作时,每当需要执行一条字节码指令时,就通过改变程序计数器的值来完成。程序中的分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。
- 程序执行过程中,会不断的切换当前执行线程,切换后,为了能让当前线程恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,并且各线程之间计数器互不影响,独立存储。
计数器的作用
- 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理
- 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候,能够知道当前线程的运行位置
- 程序计数器是唯一一个不会出现 OutOfMemoryError的内存区域,它随着线程的创建而创建,随着线程的结束而死亡
三、Java 虚拟机栈(VM Stack)
与程序计数器一样,VM Stack
虚拟机栈也是线程私有的,它的生命周期和线程相同,用于描述 Java 方法执行时的内存模型,每次方法调用的数据都是通过栈传递的。
JMM
内存区域可以粗略的区分为堆内存(Heap
)和栈内存 (Stack
)。其中栈就是VM Stack
虚拟机栈,或者说是虚拟机栈中局部变量表部分。
局部变量表主要存放了编译期可知的各种基本数据类型变量值(boolean
、byte
、char
、short
、int
、float
、long
、double
)、对象引用(reference
类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)
Java
虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。
每一次方法调用都会有一个对应的栈帧被压入 VM Stack
虚拟机栈,每一个方法调用结束后,代表该方法的栈帧会从VM Stack
虚拟机栈中弹出。
在活动线程中, 只有位于栈顶的帧才是有效的, 称为当前活动栈帧,代表正在执行的当前方法。
在JVM
执行引擎运行时, 所有指令都只能针对当前活动栈帧进行操作。虚拟机栈通过 pop
和 push
的方式,对每个方法对应的活动栈帧进行运算处理,方法正常执行结束,肯定会跳转到另一个栈帧上。
-
Java
方法有两种返回方式,不管哪种返回方式都会导致当前活动栈帧被弹出 -
return
语句- 抛出异常
Java
虚拟机栈会出现两种错误:StackOverFlowError 和OutOfMemoryError
- StackOverFlowError:当线程请求栈的深度超过
JVM
虚拟机栈的最大深度的时候,就抛出StackOverFlowError
错误。 - OutOfMemoryError:
JVM
的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError
异
四、本地方法栈(Native Method Stack)
本地方法栈用于虚拟机调用的 Native
方法
native
关键字修饰的本地方法被执行的时候,在本地方法栈中也会创建一个栈帧,用于存放该native
本地方法的局部变量表、操作数栈、动态链接、方法出口信息。方法执行完毕后,相应的栈帧也会出栈并释放内存空间。也会出现 StackOverFlowError
和 OutOfMemoryError
两种错误
五、堆(Heap)
1、概述
Heap
堆区,用于存放对象实例和数组的内存区域
Heap
堆是JVM
所管理的内存中最大的一块区域,被所有线程共享的一块内存区域。堆区中存放对象实例,“几乎”所有的对象实例以及数组都在这里分配内存
Java
世界中“几乎”所有的对象都在堆中分配,但是,随着 JIT
编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。
从JDK 1.7
开始已经默认开启逃逸分析,如果某些方法中的对象引用没有被返回或者未被外面使用(也就是未逃逸出去),那么对象可以直接在栈上分配内存
2、新生代、老年代
Heap 堆是 **垃圾收集器 GC(Garbage Collected)**管理的主要区域,因此堆区也被称为 GC堆(Garbage Collected Heap)
从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 JVM 中的堆区往往进行分代划分,例如:新生代 和 老年代。目的是更好地回收内存,或者更快地分配内存
Heap 堆区中的新生代、老年代的空间分配比例,可以通过java -XX:+PrintFlagsFinal -version
命令查看
上述输出结果结果分析
InitialSurvivorRatio = 8
新生代Young(Eden/Survivor)
空间的初始比例 = 8:代表Eden
占新生代空间的80%
;
uintx NewRatio = 2
老年代Old
/ 新生代 Young
的空间比例 = 2 : 代表老年代Old
是新生代Young
的2倍
因为新生代是由 Eden + s0 + s1 组成的,所以按照上述默认比例,如果
Eden` 区内存大小是 40M,那么两个 Survivor 区就是 5M,整个新生代区就是 50M,然后可以算出 Old 区内存大小是 100M,堆区总大小就是 150M
3、创建对象的内存分配
- 创建一个新对象,在堆中分配内存
- 大部分情况下,对象会在 Eden 区生成,当 Eden 区装满时,会触发 Young Garbage Collection,即 YGC 垃圾回收时,在 Eden 区实现清除策略,没有被引用的对象直接被回收
- 依然存活的对象会被移送到 Survivor 区
- Survivor 区分为 s0 和 s1 两块内存区域,每次 YGC 的时候,将存活的对象复制到未使用的 Survivor 空间(s0 或 s1),然后清空正在使用的空间,交换 s0 和 s1 的使用状态,每次交换时, 对象的Age+1
- 如果 YGC 要移送的对象大于 Survivor 区容量的上限,则直接移交给老年代
- 一个对象也不可能永远呆在新生代,在
JVM
中 一个对象从新生代晋升到老年代的阈值默认值是15
,可以在Survivor
区交换 14 次之后,晋升至老年代
堆区最容易出现的就是 OutOfMemoryError
错误,这种错误的表现形式会有以下两种:
OutOfMemoryError: GC Overhead Limit Exceeded
: 当JVM
花太多时间执行垃圾回收,并且只能回收很少的堆空间时,就会发生此错误。OutOfMemoryError: Java heap space
**:**假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发此错误。
此种情况,与配置的最大堆内存有关,且受制于物理内存大小。
六、元空间(Meta Space)
1、作用
用于存放 类信息、常量、静态变量、JIT 即时编译器编译后的机器代码等数据
例如:java.lang.Object
类的元信息、Integer.MAX_VALUE
常量等
2、发展历程
(1)JDK 1.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
(2)JDK 1.7
将字符串常量池、静态变量转移到了堆区。
(3)JDK 1.8
正式移除永久代,采用 Meta Space 元空间替代
元空间的本质和永久代类似,都是对JVM
规范中方法区的一种具体实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过运行参数来指定元空间的大小。
Java 8
中 PermGen
永久代为什么被移出 HotSpot JVM
?
- 由于
PermGen
内存经常会溢出,容易抛出java.lang.OutOfMemoryError: PermGen
错误; - 移除
PermGen
可以促进HotSpot JVM
与JRockit VM
的融合,因为JRockit
没有永久代
**示例1:**不断的生成新的字符串,快速的消耗内存。通过 JDK 1.6
、JDK 1.7
和 JDK 1.8
分别运行。
public class TestOOM {static String base = "ApeSource";public static void main(String[] args) {List<String> list = new ArrayList<String>();for (int i=0;i< Integer.MAX_VALUE;i++){String str = base + base;base = str;list.add(str.intern());}}
}
上述运行结果可以看出,相同的代码,在JDK 1.6
会出现“PermGen Space
”的永久代内存溢出,而在 JDK 1.7
和 JDK 1.8
中,会出现"Java heap space
"堆内存溢出,并且 JDK 1.8
中 PermSize
和 MaxPermGen
参数已经无效。因此,在 JDK 1.7
和 JDK 1.8
中,已经将字符串常量由永久代转移到堆中,并且 JDK 1.8
中已经完全移除了永久代,采用元空间来代替。
**示例2:**在 JDK 8
下重新运行一下运行测试代码TestOOM
,指定 MetaSpaceSize
和 MaxMetaSpaceSize
的大小,输出结果如下:
-XX:MetaspaceSize
**参数:主要控制Meta Space GC
发生的初始阈值,也就是最小阈值,当使用的Meta Space 空间到达
MetaspaceSize`**的时候,就会触发Metaspace的GC。- -
XX:MaxMetaspaceSize参数:最大空间,默认是没有限制的。在
jvm启动的时候,并不会分配MaxMetaspaceSize
这么大的一块内存出来,metaspace
是可以一直扩容的,直到到达MaxMetaspaceSize
七、字符串常量池
1、字符串的两种创建方式
- 第一种方式是在常量池中获取字符串对象;
- 第二种方式是直接在堆内存空间创建一个新的字符串对象
// 先检查字符串常量池中有没有"abcd",如果字符串常量池中没有,则创建一个,然后 str1 指向字符串常量池中的对象,如果有,则直接将 str1 指向"abcd"
String str1 = "apesource";
String str2 = new String("apesource"); //堆中创建一个新的对象
String str3 = new String("apesource"); //堆中创建一个新的对象System.out.println(str1==str2); //false
System.out.println(str2==str3); //false
2、intern() 方法
- 检查指定字符串在常量池中是否存在?如果存在,则返回地址,如果不存在,则在常量池中创建
String s1 = new String("Apesource");
String s2 = s1.intern(); // 查看字符串常量池中是否存在"Apesource",如果存在则返回地址,如果不存在,则在常量池中创建
String s3 = "Apesource"; // 使用常量池中的已有字符串常量"Apesource"System.out.println(s2 == s3); // true,地址相同
3、String 的拼接
String str1 = "str";
String str2 = "ing";String str3 = "str" + "ing"; // 常量池中的新字符串对象
String str4 = str1 + str2; // 在堆中创建的新字符串对象
String str5 = "string"; // 常量池中的已有字符串对象System.out.println(str3 == str4); //false
System.out.println(str3 == str5); //true
System.out.println(str4 == str5); //false
String s1 = new String("abc");
这句代码创建了几个字符串对象?
创建 1
或 2
个字符串。如果常量池中已存在字符串常量“abc
”,则只会在堆空间创建一个字符串常量“abc
”
如果常量池中没有字符串常量“abc
”,那么它将首先在池中创建,然后在堆空间中创建,因此将创建总共2 个字符串对象
相关文章:

JVM 虚拟机 ----> Java 内存模型(JMM)
文章目录 Java 内存模型(JMM)一、运行时数据区域划分二、程序计数器(Program Counter Register)计数器的作用 三、Java 虚拟机栈(VM Stack)四、本地方法栈(Native Method Stack)五、…...

指针-字符串替换
任务描述 从标准输入读入数据,每行中最多包含一个字符串 “_xy_”,且除了字符串“_xy_”外,输入数据中不包括下划线字符,请将输入行中的 “_xy_” 替换为 “_ab_”, 在标准输出上输出替换后的结果;若没有进行过满足条…...

docker 网络(单机环境)
文章目录 深入理解 Namespace什么是NamespaceNamespace当中的 Network Namespace Libcontainerdocker 网络基础创建两个命名空间创建网络接口 veth pair命名空间添加 veth 接口为 veth 接口分配 IP启动 veth 接口相互 ping bridge 网络搭建网络环境查看docker0 网桥创建网桥 br…...

14、二叉树的morris遍历等
统计热词 有一个包含100亿个URL的大文件,假设每个URL占用64B,请找出其中所有重复的URL 【补充】 某搜索公司一天的用户搜索词汇是海量的(百亿数据量),请设计一种求出每天热门Top100 词汇的可行办法 多个小文件的大根堆,然后把每…...

BeanFactory与ApplicationContext
BeanFactory与ApplicationContext的区别 使用Alt Ctrl U查看java类图 什么是BeanFactory接口 他是ApplicationContext的父接口他才是Spring 的核心容器,主要的ApplicationContext功能的实现都间接通过BeanFactory接口来实现 在ApplicationContext类中方法的实现是…...
【计算机网络】 粘包问题
文章目录 为什么会产生粘包问题?解决办法先发包大小再发包内容代码示例 为什么会产生粘包问题? tcp是数据流传输,是一种没有边界的,可以合并的传输数据方式。合并就要能拆开,拆不开就是粘包。 解决办法 设置标志位&a…...
valgrind massif 详解(内存分配释放分析)
参考 https://valgrind.org/docs/manual/ms-manual.html 使用格式 valgrind --toolmassif [--massif-opts] prog [prog-args]目的 记录每一次的malloc, free; 概念: malloc申请内存, 实际分配内存(字节对齐, 分配器的记录头, 等等原因) 对内存进行分析, 优化, 以达到资源…...

使用命令行创建一个vue项目卡住不动如何解决
问题 在使用命令去创建一个vue项目, 出现下面卡住不动的一个状态。 解决方案一 首先先ctrlc停止进入创建好的项目文件手动输入npm install 、npm run dev如果npm run dev 的时候 出现 ‘vite’ 相关的错误查看node版本是否是最新的稳定版本node -v查看安装源是否…...

七天学会C语言-第一天(C语言基本语句)
一、固定格式 这个是C程序的基本框架,需要记住!!! #include<stdio.h>int main(){return 0; }二、printf 语句 简单输出一句C程序: #include<stdio.h> int main(){printf("大家好,&quo…...

vue项目部署,出现两个ip的原因
我宁愿靠自己的力量打开我的前途,而不愿求有力者的垂青。——雨果 tags: 篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue项目部署,出现两个ip的原因相关的知识,希望对你有一定的参考价值。 参考技术A 在部署v…...

无涯教程-JavaScript - ASIN函数
描述 ASIN函数返回给定数字的反正弦或反正弦,并返回以弧度表示的Angular,介于-π/2和π/2之间。 语法 ASIN (number)争论 Argument描述Required/OptionalNumberThe sine of the angle you want and must be from -1 to 1.Required Notes 如果您希望ASIN函数返回的Angular以…...

MYSQL的SQL优化
insert语句 开启事务 手动控制事务 start transaction; insert into tb_test values(1,Tom),(2,Cat),(3,Jerry); insert into tb_test values(4,Tom),(5,Cat),(6,Jerry); insert into tb_test values(7,Tom),(8,Cat),(9,Jerry); commit; 内存插入 load命令中用 fields te…...
lintcode 553 · 炸弹袭击【中等 数组+bfs+模拟】
题目 https://www.lintcode.com/problem/553 给定一个二维矩阵, 每一个格子可能是一堵墙 W,或者 一个敌人 E 或者空 0 (数字 0), 返回你可以用一个炸弹杀死的最大敌人数. 炸弹会杀死所有在同一行和同一列没有墙阻隔的敌人。 由于墙比较坚固,所以墙不会被摧毁.你只…...

第一章 计算机系统概述 八、虚拟机
目录 一、传统虚拟机的结构 二、两类虚拟机管理程序 (1)定义: (2)区别:(考点) 一、传统虚拟机的结构 二、两类虚拟机管理程序 (1)定义: &…...

桶装水送水多水站送水员公众号h5开发
桶装水送水多水站送水员公众号h5开发 界面简洁易懂用户容易接受。 独家一户一码全家都能订水。 多个水站运营可按距离选择绑定。 三种支付方式水票、微信、到付。 强大员工系统老板坐享其成。 自由跑跑模式可招兼职送水员接单。 一户一码、全家享用 一户一码,精准…...

【JavaEE】多线程(二)
多线程(二) 文章目录 多线程(二)第一个多线程程序观察线程sleep创建线程继承Thread类,重写run方法实现Runnable, 重写run继承Thread,重写run实现Runnable,重写run基于lambda表达式 T…...
OkHttp 根据服务器返回的的过期时间设置缓存
据返回的缓存时间来缓存响应,可以通过使用OkHttp的CacheControl和ResponseCacheInterceptor来实现。以下是一个示例代码: // 创建缓存目录和缓存对象 File cacheDirectory new File(context.getCacheDir(), "http-cache"); int cacheSize 1…...

智能远程监考方案助力企业考试化繁为简
在音视频数字化之旅中,轻装上阵。 近年来,在数字化浪潮之下,远程考试频繁成为各领域热词,各企业也纷纷改革求新,将原本的企业内部考试转移到线上,从而获取更低廉的组考成本,更高的管理效率&…...

基于matlab实现的额 BP神经网络电力系统短期负荷预测未来(对比+误差)完整程序分享
基于matlab实现的额 BP神经网络电力系统短期负荷预测 完整程序: clear; clc; %%输入矢量P(15*10) P[0.2452 0.1466 0.1314 0.2243 0.5523 0.6642 0.7105 0.6981 0.6821 0.6945 0.7549 0.8215 0.2415 0.3027 0; 0.2217 0.1581 0.1408 0.23…...
WPF的_Expander控件
WPF Expander 是 WPF(Windows Presentation Foundation)框架中的一个控件,用于实现可以展开和折叠内容的可折叠面板。 Expander 控件通常由一个展开/折叠的标题(Header)和一个显示/隐藏的内容部分(Content…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...