jvm内存模型(总结性质)

jvm内存模型。  
设计目的: 屏蔽掉硬件和操作系统的内存访问差异。以达到java程序在各种平台下达到一致的访问效果。
这个c/c++ 不同,后者是直接使用物理硬件和操作系统的内存模型。

设计原则:围绕着并发过程总如何处理原子性 可见性 有效性这三个特征来建立。

具体的目标就是定义程序中变量的访问规则,即在虚拟机将变量存储到内存和从内存中取出变量的底层细节。所以内存模型本质上 是对特定内存和高速缓存 进行读写访问过程的抽象。这个过程是依据特定的操作协议。高速缓存一致性 也是有根据协议来保证的。


一、从按照数据区域的用途划分 :这种划分方式同时体现了不同区域所管理的数据的 生命周期不同。

线程私有: 程序计数器 虚拟机栈 本地方法栈。
线程共享:java堆 方法区(运行时常量池  )直接内存。


程序计数器:
程序计数器是线程私有的,是为了解决线程并发情况下,线程切换后能恢复到正确的执行位置。
如果是一个方法正在执行的时候,计数器记录的是字节码指令的地址。
分支 循环 跳转 异常处理的时候也要用到程序计数器。
形象的说是行号指示器。通过改变计数器的值来选择下一条字节指令。

虚拟机栈:
虚拟机栈是 java方法执行的内存模型。方法执行时候会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口。每个方法的调用对应一个栈帧在虚拟机栈中入栈和出栈的过程。

java堆:
从垃圾回收的角度可以细分为,新生代和老年代,
java堆中存储的是 对象数据 对象头,指向元数据的指针。

方法区:
方法区是线程共享的,方法区也叫永久代。目前变成了matespace。方法区存放的数据分两种情况:

一是class文件二进制流(未执行类加载) 其中包含常量池。
二是在类加载后生成:
类元数据信息(就是InstanceKlass对象 包括字段和虚方法表) 静态变量(其实就是InstanceKlass的成员)
常量池这时变成运行时常量池。
即时编译器的代码。

直接内存:
直接内存是堆外内存,在nio中,引用一种基于通道和缓冲区的io方式会用到堆外内存,具体是利用directByteBuffer对象作为这块内存的直接引用进行操作。直接内存不受不受java堆参数限制以及gc管理。



二、根据变量的访问规则的角度划分:

可简单分为 主内存和 工作内存。 主内存是线程共享。 工作内存是线程私有。
主内存对应于java堆中对象实例的数据部分。工作内存对应虚拟机栈部分区域。
主内存对应于物理内存。工作内存对应于寄存器和高速缓存和物理内存

由于java内存模型规定所有变量都保存在主内存,线程间变量访问都通过主内存进行的。所以就有了内存间交互。

内存间交互:
即一个变量从主内存拷贝到工作内存,工作内存同步回主内存的操作细节。定义8中操作来完成。这8个操作都是原子的。
主内存读取到工作内存。 read和load    
工作内存写入到主内存: store和write。
还有两个lock unlock  作用于主内存对一个变量进行加锁和解锁的指令。
use assign  作用于工作内存,在执行引擎用到这个值时候 和 给该变量赋值的操作。。 可以看到线程对变量的读取和赋值操作 都是在工作内存 中完成。


三、jvm内存模型的设计原则是围绕着并发过程总如何处理原子性、可见性、有序性这三个并发特征来建立。

原子性:内存交互操作必须保证原子性。即各种操作,在并发时 是会出错的。这个操作也是不可再分的。

基本的原子性变量操作,包括 read load assign use store write指令。
更大范围的原子性保证是通过锁机制。也就是monitorenter 指令。也就是synchronized关键字。

可见性:是指当一个线程修改了共享变量的值,其他线程都能够立刻见到。volatile synchronized final 都可以是实现。

有序性: 是指线程内表示串行语义,这个串行语义不能防止 指令重排序,指令重排序是机器级别的优化操作,是指提前执行这条汇编。 指令重排序的前提只保证 在当前线程内,处理指令的依赖并保证正确的执行结果。 而站在另一个线程观察,可能就会得到错误的 结果。 参考 一个线程引用另一个线程布尔值的例子。

所以这个无序的原因是 在多线程下 由于指令重排序 会导致逻辑上的错误。解决是 使用volatile防止指令重排序,以及立即可见性。使用synchronized 能保证多个线程串行的访问。

关于有序性的保证 首先有一些天然的先行发生原则。先行发生原则 保证了这几种规则下的确定的执行顺序,如果不在这个原则列表中,那么就需要通过以上手段来保证有序性。

代码次序先发生 和先行发生没有关系:
两个线程分别调用getter setter方法。不能保证,是getter还是setter先调用。 也就是说 时间上先发生,不一定会先行发生。 需要用管程锁定规则,或者volatile变量规则锁定。

指令重排序 在多线程下引起错误的例子:
一个线程中的一段代码,先进行一段资源初始化,之后给Boolean变量赋值为true表示初始化完成,然后其他线程得到该值为true时即可以调用该资源。 但是经过指令重排序,该Boolean 被提前赋值为true,而不是最后执行,那么其他线程便会得到一个未初始化的资源。

还有一个dcl 双锁检测的例子。






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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值