7. Java内存模型

       它的意义是来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效。在此之前,主流程序语言(C/C++等)直接使用物理硬件和操作系统的内存模型。因此,会由于不同平台上的内存模型差异,有可能导致程序在不同平台上运算结果不同。因此在某些场景下必须对不同的平台来编写程序。

1. 主内存与工作内存

        Java内存模型主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到主存和从内存中读取出变量这样的底层细节。此处变量包括了实例字段、静态字段和构成数组对象的元素,但是不包括局部变量和方法参数。因为后者是线程私有的,不会被共享,也就不存在并发的问题。

        Java内存模型规定:所有的变量都存储在主内存中。每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝。线程对变量的所有操作(读取、赋值)都必须在工作内存中完成,不能直接读写主内存中的变量。不同线程之间也无法直接访问对方工作内存中的变量,线程之间变量值的传递均需要通过主内存来完成。三者之间的关系如下图:

                                                  

 2. 内存间的交互操作

       Java内存模型定义了8种操作来完成内存间的交互,且虚拟机必须保证下面的每一种操作是原子的,不可分割的(long和double例外):

  • lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
  • unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  • read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
  • load(载入):作用于主内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
  • use(使用):作用于主内存的变量,它把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用的变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于主内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时将会执行这个操作。
  • store(存储):作用于主内存的变量,它把工作内存中一个变量的值传递到主内存中,以便随后的write操作使用。
  • write(写入):作用于主内存的变量,它把store操作从工作内存中获得的变量的值放入主内存的变量中。

        若把一个变量从主内存复制到工作内存中,要顺序执行read和load操作;若要把变量从工作内存同步回主内存,要顺序执行store和write操作。注意,read和load、store和write之间可以插入其他指令。

注意:在JSR-133文档中,已经放弃采用这8种操作去定义内存模型的访问协议了。

3. volatile

 它是Java虚拟机提供的最轻量级的同步机制。当把一个变量定义为volatile后,它将具备两种特性:

  • 第一保证变量对所有线程的可见性。可见性是指当一个线程修改了这个变量的值,新值对其他所有线程来说是可以立即得知的。普通话标本量的值不能做到这一点,普通变量的值在线程间传递均需要通过主内存来完成。但要保证原子性,仍然要通过加锁来实现。
  • 第二是禁止指令重排序优化。普通变量仅仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能得到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致。

       volatitle 变量读操作的性能消耗与普通变量几乎没有什么差别,但是写操作则可能会慢一些。但是大多数场景下volatile 的总开销要比锁低。

       Java内存模型允许竟没有被 volatile 修饰的64位数据的读写操作划分为两次32位的操作进行,即允许虚拟机实现选择可以不保证64位数据类型的laod、store、read和write这4个操作的原子性。但是在各平台的商用虚拟机几乎都选择把64位数据的读写操作作为原子操作来对待。

4. 原子性、可见性和有序性

        原子性:由Java内存模型来直接保证的原子性变量操作包括read、load、assign、use、store和write。可以大致认为基本数据类型的访问读写具备原子性(long和double除外)。

        可见性:是指当一个线程修改了共享变量,其他线程能够立即得知这个修改。Java 内存模型是通过在变量修改后将新值同步回主内存,在变量读取之前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的,无论是普通变量还是volatile 变量都是如此。普通变量与 volatile 变量的区别是,volatile 的特使规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。因此可以说volatile 保证了多线程操作时变量的可见性,而普通变量则不能保证这一点。另外,synchronized 和 final 也可以实现可见性。final关键字可见性是指:被 final 修饰的字段在构造器中一旦初始化完成,并且构造器没有吧this的引用传递出去,那在其他线程中中就能看见 final 字段的值。

       有序性:Java提供了 volatile 和 synchronized 关键字来保证线程之间操作的有序性, volatile关键字本身就包含了禁止指令重排的语义,而 synchronized 则是由“一个变量在同一时刻只允许一条线程对其进行lock操作”这条规则获得的,这条规则决定了持有同一锁的两个同步块只能串行地进入。

5. 先行发生原则(happens-before)

        它是Java内存模型中定义的两项操作之间的偏序关系。如果说操作A先行发生于操作B,换句话说就是,操作A产生的影响能被操作B观察到。影响包括:修改了内存中共享变量的值、发送了消息、调用了方法等。

        下面是Java内存模型下一些“天然的”先行发生关系,这些先行发生关系五须任何同步器协助就已经存在,可以直接使用。若两个操作之间的关系不在此列,并且无法从下列规则推导出来的话,它们的顺序就没有顺序性保障,虚拟机可以对它们随意地进行重排序。

  1. 程序次序规则:在一个线程内,按照代码顺序,书写在前面的操作先行发生于后面的操作。
  2. 管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。这里必须是同一个锁,而“后面”是指时间上的先后顺序。
  3. volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作。后面”是指时间上的先后顺序。
  4. 线程启动规则:Thread对象的 start() 方法先行发生于此线程的每一个操作。
  5. 线程终止规则:线程中所有的操作都先于对此线程的终止检测。
  6. 线程中断规则:对线程interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
  7. 对象中结规则:一个对象的初始化完成先行发生于它的finalize()方法的开始。
  8. 传递性:如果操作A先行发生于操作B,操作B先行发生于操作C,则操作A先行发生于操作C。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为什么要学JVM1、一切JAVA代码都运行在JVM之上,只有深入理解虚拟机才能写出更强大的代码,解决更深层次的问题。2、JVM是迈向高级工程师、架构师的必备技能,也是高薪、高职位的不二选择。3、同时,JVM又是各大软件公司笔试、面试的重中之重,据统计,头部的30家互利网公司,均将JVM作为笔试面试的内容之一。4、JVM内容庞大、并且复杂难学,通过视频学习是最快速的学习手段。课程介绍本课程包含11个大章节,总计102课时,无论是笔试、面试,还是日常工作,可以让您游刃有余。第1章 基础入门,从JVM是什么开始讲起,理解JDK、JRE、JVM的关系,java的编译流程和执行流程,让您轻松入门。第2章 字节码文件,深入剖析字节码文件的全部组成结构,以及javap和jbe可视化反解析工具的使用。第3章 类的加载、解释、编译,本章节带你深入理解类加载器的分类、范围、双亲委托策略,自己手写类加载器,理解字节码解释器、即时编译器、混合模式、热点代码检测、分层编译等核心知识。第4章 内存模型,本章节涵盖JVM内存模型的全部内容,程序计数器、虚拟机栈、本地方法栈、方法区、永久代、元空间等全部内容。第5章 对象模型,本章节带你深入理解对象的创建过程、内存分配的方法、让你不再稀里糊涂。第6章 GC基础,本章节是垃圾回收的入门章节,带你了解GC回收的标准是什么,什么是可达性分析、安全点、安全区,四种引用类型的使用和区别等等。第7章 GC算法与收集器,本章节是垃圾回收的重点,掌握各种垃圾回收算法,分代收集策略,7种垃圾回收器的原理和使用,垃圾回收器的组合及分代收集等。第8章 GC日志详解,各种垃圾回收器的日志都是不同的,怎么样读懂各种垃圾回收日志就是本章节的内容。第9章 性能监控与故障排除,本章节实战学习jcmd、jmx、jconsul、jvisualvm、JMC、jps、jstatd、jmap、jstack、jinfo、jprofile、jhat总计12种性能监控和故障排查工具的使用。第10章 阿里巴巴Arthas在线诊断工具,这是一个特别小惊喜,教您怎样使用当前最火热的arthas调优工具,在线诊断各种JVM问题。第11章 故障排除,本章会使用实际案例讲解单点故障、高并发和垃圾回收导致的CPU过高的问题,怎样排查和解决它们。课程资料课程附带配套项目源码2个159页高清PDF理论篇课件1份89页高清PDF实战篇课件1份Unsafe源码PDF课件1份class_stats字段说明PDF文件1份jcmd Thread.print解析说明文件1份JProfiler内存工具说明文件1份字节码可视化解析工具1份GC日志可视化工具1份命令行工具cmder 1份学习方法理论篇部分推荐每天学习2课时,可以在公交地铁上用手机进行学习。实战篇部分推荐对照视频,使用配套源码,一边练习一遍学习。课程内容较多,不要一次性学太多,而是要循序渐进,坚持学习。      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值