JVM和JMM内存模型的区别

JVM内存模型

java虚拟机,可以让class文件在不同操作系统上运行。
1、:主要存储我们的创建的对象
2、方法区:存储类信息、常量、静态变量等
3、:jvm会在栈上给每个线程分配独立的内存空间,来存储线程的局部变量,每个方法执行时,会给其分配一个栈帧。
栈主要包含
3.1、局部变量表:存储局部变量
3.2、操作数栈:程序运行过程中,要做操作的临时中转存储空间
3.3、动态链接:存储方法在方法区中的地址(程序运行时需要将符号引用转为直接引用)
程序执行顺序:栈的结构与数据结构栈一致,是FILO;先执行main方法,则会将main方法的栈帧先入栈,再执行test方法入栈;当test方法执行完后,test对应的栈帧要出栈,即释放该栈帧空间

public class Demo{
    public int test() {  //一个方法对应一块栈帧内存区域
        int a = 1;
        int b = 2;
        int c = (a + b) * 10;
        return c;
    }

    public static void main(String[] args) {
        Demo demo = new Demo ();
        demo.test();
    }
}

分析下test方法的字节码文件

public int test();
Code:
0: iconst_1
# 将int类型常量1(数值1)压入操作数栈
1: istore_1
# 将int类型值存入局部变量1 ,将数值1从操作数栈取出,存入局部变量a,即a=1
2: iconst_2
# 将int类型常量2(数值2)压入操作数栈
3: istore_2
# 将int类型值存入局部变量2 ,将数值2从操作数栈取出,存入局部变量b,即b=2
4: iload_1
# 从局部变量1中装载int类型值,从局部变量表中取出a的值,压入操作数栈
5: iload_2
# 从局部变量2中装载int类型值,从局部变量表中取出b的值,压入操作数栈
6: iadd
# 从操作数栈中取出a、b的值,在cpu寄存器上执行运算得到3,然后将3再次压入操作数栈
7: bipush 10
# 将一个8位带符号整数压入栈,将10压入操作数栈
9: imul
# 执行int类型的乘法,将操作数栈上的3和10取出,在cpu寄存器上执行运算得到30,
将30再次压入操作数栈
10: istore_3
# 将int类型值存入局部变量3,将数值30从操作数栈取出,存入局部变量表,即c=30
11: iload_3
# 从局部变量3中装载int类型值,从局部变量表中取出c的值,压入操作数栈
12: ireturn
# 从方法中返回int类型的数据,将操作数栈上的c的值30返回

JMM内存模型

JMM描述的是一种抽象的概念,规定了一个线程怎么样可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量。
JMM是围绕原子性、有序性、可见性展开的。
在这里插入图片描述

volatile可见性实现原理

volatile修饰的变量的read、load、use操作和assign、store、write必须是连续的,即修
改后必须立即同步回主内存,使用时必须从主内存刷新,由此保证volatile变量操作对多线程
的可见性。

硬件层面的实现

在这里插入图片描述

早期使用总线锁来保证
1、共享变量X存于主内存空间中
2、CPU0从主内存中读取到数据,获取到总线锁,从L3一直读取到L1中
3、因为CPU0占用了锁,所以此时CPU1无法再次获取数据

后期使用MESI缓存一致协议
M 修改、E 独享、S 共享、I 无效
我们知道JVM创建的线程,在内核空间上也会创建一个对应的线程,最终交由cpu来执行。如上图所示,线程A对象CPU0来执行,线程B对应CPU1来执行。现在线程A、线程B要同时修改共享变量x的值,volatile保证可见性其实就是用缓存一致性协议来做的。

参考CPU结构来理解
volatile修饰X后,在硬件源语上会有一个lock前缀;CPU一直会监听bus总线里面的消息
1、共享变量X存于主内存空间中
2、CPU0从主内存中读取X,经过总线,发现被lock前缀修饰了,则会被CPU1监听到有其他CPU在读取数据。CPU0读取到数据后会在缓存中有个X副本,并标记状态E(此时只有CPU0持有这个副本)
3、CPU1从主内存中读取X,经过总线,发现被lock前缀修饰了,会被CPU0监听到,CPU0将缓存中的X副本状态修改为S,CPU1读取到数据后也会在缓存中有个X副本,此时标记状态S
4、CPU0上X存储在自己缓存中的缓存行上,CPU1上X存储在自己缓存中的缓存行上,两个CPU都对各自的存储X的缓存行进行加锁。
4.1、比如CPU0修改X=2,会先获取获取缓存行上的锁,再将状态修改为E,然后向外部发送一个本地写缓存消息;此时会被CPU1感知到,CPU1会将自己的X副本状态标记为I失效,并丢弃掉;
4.2、若CPU0、CPU1同时修改X的值呢,两个CPU都会向外部发送本地写缓存消息,两个消息都会经过bus总线,bus总线会进行总线裁决,来决定让哪个CPU来获得修改的权限。若CPU1获得,则CPU0的X副本状态标记为I失效,并丢弃掉;
5、若CPU0先修改了X的值,则会将X的值写入主内存,然后CPU1会重新从主内存中读取X的值

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值