Java内存模型
2023-04-30 11:18:09 1 举报
AI智能生成
登录查看完整内容
Java内存模型 思维导图
作者其他创作
大纲/内容
分支主题
实际上,工作内存、主内存都位于物理内存中
作用于主内存的变量,把一个变量标识为一条线程独占状态
lock(锁定)
作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
unlock(解锁)
作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
read(读取)
作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中
load(载入)
作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作
use(使用)
作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作
assign(赋值)
作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作
store(存储)
作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中
write(写入)
内存间交互操作
内存区块
类信息
常量
静态变量
及时编译器编译后的代码
存储多种信息
被描述为堆的逻辑部分
容易遇到内存溢出问题
1.7中将字符串常量池移出了
1.7之前是使用永久代来实现的方法区
当方法区无法满足内存分配需求时OOM
用于存放class中的常量池数据
不要求常量一定是编译期产生
利用的最多的就是string.intern()
运行期间也可以放入新的常量
具备动态性
当无法申请到内存时OOM
运行时常量池
通过 -XX:MaxPermSize 设置大小
方法区
在虚拟机启动时创建
用来存放对象实例
使用分代回收算法
空间非连续
会抛出OOM
在32位和64位虚拟机中分别为32bit和64bit
官方名 Mark Word
与对象自身定义的数据无关的额外存储成本
根据对象状态复用自己的存储空间
对象自身运行时数据
指向它的类元数据
虚拟机通过这个指针来确定对象是哪个类的实例
类型指针
还有一块用于记录数组长度的数据
如果对象是数组
对象头
包括父类继承的还是子类中定义的
程序代码中定义的各种类型的字段内容
实例数据
由于hotspot VM的自动内存管理系统要求对象的起始地址必须是8字节的整数倍
当实例数据部分没有对齐时,使用对齐填充补充
对齐填充
对象的内存布局
通过-Xmx和-Xms控制大小
堆
1.8
不在虚拟机中分配,直接使用本地内存
大小只与配置和本地内存大小相关
metaspace
线程共享
记录线程锁执行字节码的行号
java方法记录的是正在执行的虚拟机字节码指令地址
native方法的话,计数器值为空
唯一一个没有OOM的区域
程序计数器
生命周期与线程相同
保存线程运行状态
描述的是Java方法执行的内存模型
存放可知的基本数据类型
对象引用地址
double
long
超过64位的数据占用两个变量空间
空间以32位为粒度
局部变量表
操作数栈
动态链接
返回地址
最小单位是栈帧
非连续内存空间
当栈扩展无法申请到做够的内存时
OOM
当线程请求的栈深度超过虚拟机栈深度
StackOverFlow
会抛出异常
虚拟机栈(VM栈)
为虚拟机提供Native方法服务
本地方法栈
线程隔离(私有)
锁的释放 - 获取建立的happens-before关系
锁释放和获取的内存语义
锁内存语义的实现
concurrent包的实现
lock 锁
可见性:任意线程都volatile最新值都可见
原子性:对任意单个volatile变量的读/写具有原子性
特性
当写一个volatile变量时,JMM会吧该线程对应的本地内存中的共享变量刷新到主内存中
内存语义
volatile内存语义的实现
JSR-133 增强volatile的内存语义
volatile
在构造函数内对一个final域的写入,与将final对象赋值给引用变量,这两个操作之间不能重排序
初次读取final域的对象引用,与初次读取final域对象,这两个操作之间不能重排序
final域的重排序规则
JMM禁止编译器把final域的写重排序到构造函数之外
JMM会在final域的写之后,构造函数return之前,插入一个storestore屏障。禁止final域的写重排序到构造函数之外
规则
在对象引用为任意线程可用之前,对象的final域已经被正确初始化,普通域不具有这个保障
确保
写final域的重排序规则
在读一个final域之前,一定先读该final域的对象的引用
读final域的重排序规则
在构造函数内对一个final引用的对象的成员域的写入,与随后在构造函数外把构造的对象的引用赋值给一个引用变量,这两个操作之间不能重排序
如果final域是引用类型
为什么final引用不能从构造函数内“逸出”
final语义在处理器中的实现
只要对象是正确构造的(被构造对象的引用在构造函数中没有溢出),那么不需要使用同步(指lock与volatile的使用)就可以保证任意函数都可以看到final域在构造函数内被初始化后的值
JSR133 为什么要增强final的语义
final
同步原语
数据依赖性
不管怎么重排序,单线程程序的执行结果不能发生改变
as-if-serial语义
程序顺序规则
重排序对多线程的影响
内存屏障
禁止重排序
重排序
数据竞争与顺序一致性保证
一个线程中的所有操作必须按照程序的顺序来执行
(不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见
顺序一致性内存模型
同步程序的顺序一致性
顺序一致性模型保证单线程内的操作会按程序的顺序执行,而JMM不保证单线程内的操作会按程序的顺序执行
顺序一致性模型保证所有线程只能看到一致的操作执行顺序,而JMM不保证所有线程能看到一致的操作执行顺序
未同步程序的执行特性
顺序一致性
L2 Cache更大一些,如256K,速度稍慢,一般每个核都要一个独立的L2 Cache
L3 Cache多核共用
缓存结构
M(Modified):这行数据有效,数据被修改了,和内存中的数据不一致,数据只存在于本Cache中
E(Exclusive):这行数据有效,数据和内存中的数据一致,数据只存在于本Cache中
S(Shared):这行数据有效,数据和内存中的数据一致,数据存在于很多Cache中
I(Invalid):这行数据无效
MESI协议
缓存一致性
如果没有利用好缓存行,可能会遇到性能问题
缓存最小操作单位
64字节
$ cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size
查询缓存行大小
缓存行 Cache line
多线程时,如需修改“共享同一缓存行变量”,就会无意中影响彼此的性能(互斥)
原因
单个数据填充满一个Cache Line(空间换时间)
自动补齐缓存行
解决原理
1.7
解决方案
伪共享(False Sharing)
数据预取
第一次访问数据,在cache中根本不存在,所以cache miss,可通过 prefetch 解决
cache冲突,通过补齐解决(伪共享的产生)
cache满,一般需要减少操作的数据大小,尽量按数据的物理顺序访问数据
缓存失效
CPU缓存
消息传递
共享内存
线程通信
显式
隐式
线程同步
happens-before
基础
Java内存模型
0 条评论
回复 删除
下一页