前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java内存模型(JMM)

Java内存模型(JMM)

作者头像
用户3467126
发布2019-07-12 15:25:01
5.6K2
发布2019-07-12 15:25:01
举报
文章被收录于专栏:爱编码爱编码
简介

Java内存模型规范了Java虚拟机与计算机内存是如何协同工作的。Java虚拟机是一个完整的计算机的一个模型,因此这个模型自然也包含一个内存模型——又称为Java内存模型。

为什么要有内存模型
现代的计算机有多级缓存

在CPU访问存储设备时,无论是存取数据抑或存取指令,都趋于聚集在一片连续的区域中,这就被称为局部性原理。

时间局部性(Temporal Locality):如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。比如循环、递归、方法的反复调用等。 空间局部性(Spatial Locality):如果一个存储器的位置被引用,那么将来他附近的位置也会被引用。 比如顺序执行的代码、连续创建的两个对象、数组等

多核CPU多级缓存一致性协议MESI

多核CPU的情况下有多个一级缓存,如何保证缓存内部数据的一致,不让系统数据混乱。这里就引出了一个一致性的协议MESI。

注意: 对于M和E状态而言总是精确的,他们在和该缓存行的真正状态是一致的,而S状态可能是非一致的。

重排序

在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。

重排序分三类:

1、编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。 2、指令级并行的重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。 3、内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

Java内存模型(JMM)

Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。

各部分的特点和作用如下:

为了保证共享内存的正确性(可见性、有序性、原子性),内存模型定义了共享内存系统中多线程程序读写操作行为的规范。通过这些规则来规范对内存的读写操作,从而保证指令执行的正确性。目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。

保证原子性

在Java中,为了保证原子性,提供了两个高级的字节码指令monitorentermonitorexit。这两个字节码,在Java中对应的关键字就是synchronized

因此,在Java中可以使用synchronized来保证方法和代码块内的操作是原子性的。

保证可见性

Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值的这种依赖主内存作为传递媒介的方式来实现的。

Java中的volatile关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次是用之前都从主内存刷新。

下面是保守策略下,volatile 写操作 插入内存屏障后生成的指令序列示意图:

下面是在保守策略下,volatile 读操作 插入内存屏障后生成的指令序列示意图:

上述 volatile 写操作和 volatile 读操作的内存屏障插入策略非常保守。在实际执行时,只要不改变 volatile 写-读的内存语义,编译器可以根据具体情况省略不必要的屏障。

保证有序性

在Java中,可以使用synchronized和volatile来保证多线程之间操作的有序性。实现方式有所区别: volatile关键字会禁止指令重排。 synchronized关键字保证同一时刻只允许一条线程操作。

说到有序性,注意,我们说有序性可以通过 volatile 和 synchronized 来实现,但是我们不可能所有的代码都靠这两个关键字。实际上,Java 语言已对重排序或者说有序性做了规定,这些规定在虚拟机优化的时候是不能违背的。

Happen-Before 原则如下:

1.程序次序原则:一个线程内,按照程序代码顺序,书写在前面的操作先发生于书写在后面的操作。 2.volatile 规则:volatile 变量的写,先发生于读,这保证了 volatile 变量的可见性。 3.锁规则:解锁(unlock) 必然发生在随后的加锁(lock)前。 4.传递性:A先于B,B先于C,那么A必然先于C。 5.线程的 start 方法先于他的每一个动作。 6.线程的所有操作先于线程的终结。 7.线程的中断(interrupt())先于被中断的代码。 8.对象的构造函数,结束先于 finalize 方法。

Java8的不同
Metaspace(元空间)

在JDK1.8中,永久代已经不存在,存储的类信息、编译后的代码数据等已经移动到了MetaSpace(元空间)中,元空间并没有处于堆内存上,而是直接占用的本地内存(NativeMemory)

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。 不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存

元空间的大小仅受本地内存限制,可以通过以下参数来指定元空间大小:

-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值 -XX:MaxMetaspaceSize,最大空间,默认是没有限制的 -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集 -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

堆内存划分

在JDK1.7以及其前期的JDK版本中,堆内存通常被分为三块区域:

Young Generation、Old Generation、Permanent Generation for VM Matedata

在JDK1.8中把存放元数据中的永久内存从堆内存中移到了本地内存中,JDK1.8中JVM堆内存结构就变成了如下:

参考文章

更多相关知识可以查看以下好文章:

https://www.cnblogs.com/yanlong300/p/8986041.html http://www.54tianzhisheng.cn/2018/02/28/Java-Memory-Mode https://blog.csdn.net/hollis_chuang/article/details/80880118 https://www.cnblogs.com/cjsblog/p/9850300.html

最后

如果对 Java、大数据感兴趣请长按二维码关注一波,我会努力带给你们价值。觉得对你哪怕有一丁点帮助的请帮忙点个赞或者转发哦。 关注公众号【爱编码】,小编会一直更新文章的哦

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱编码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么要有内存模型
  • 现代的计算机有多级缓存
  • 多核CPU多级缓存一致性协议MESI
  • 重排序
  • Java内存模型(JMM)
  • 保证原子性
  • 保证可见性
  • 保证有序性
  • Java8的不同
  • Metaspace(元空间)
  • 堆内存划分
  • 参考文章
  • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档