Java 内存模型与内存结构

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/ityard/article/details/101693717

1 Java内存模型

1.1 简介

Java 内存模型(JMM)主要是为了规定线程和内存之间的一些关系;根据 JMM 的设计,系统存在一个主内存(Main Memory)和工作内存(Work Memory),Java中所有变量都储存在主内存中,对于所有线程都是共享的;每条线程都有自己的工作内存,工作内存中存储了该线程已读、写共享变量的副本,工作内存是 JMM 的一个抽象概念,主要包括:缓存,写缓冲区,寄存器以及其他的硬件和编译器优化;线程对所有变量的操作都是在工作内存中进行的,线程之间无法相互直接访问,变量传递均需要通过主内存完成。JMM 示意图如下:

1.2 JMM带来了哪些问题?

1)可见性问题

CPU 中运行的线程从主内存中拷贝共享对象 obj 到它的 CPU 缓存,把对象 obj 的 count 变量改为 2,但这个变更对运行在右边 CPU 中的线程不可见,因为这个更改还没有 flush 到主内存中,要解决共享对象可见性这个问题,可以使用 volatile 或加锁(如:synchronized),来保证可见性。

2)竞争问题

线程A和线程B共享一个对象 obj,假设线程A从主存读取 Obj.count变量到自己的 CPU 缓存,同时,线程B也读取了 Obj.count 变量到自己的 CPU 缓存,并且这两个线程都对 Obj.count 做了加 1 操作;此时,Obj.count 加 1 操作被执行了两次,不过都在不同的 CPU 缓存中,如果这两个加 1 操作是串行执行的,那么 Obj.count 变量便会在原始值上加 2,最终主存中的 Obj.count 的值会是 3;然而如果是并行操作,不管是线程 A 还是线程 B 先 flush 计算结果到主存,最终主内存中的 Obj.count 只会增加 1 次变成 2;可以使用加锁( 如:synchronized) 解决此问题,来保证一致性。

3)重排序问题

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

可以使用 volatile 或加锁(如:synchronized)来保证有序性。

2 Java内存结构

先看一下结构图:

从图中可以看出Java内存结构包括五大区域:堆、方法区、虚拟机栈、本地方法栈、程序计数器,其中堆、方法区线程共享,虚拟机栈、本地方法栈、程序计数器线程私有。

1)堆

堆是Java虚拟机管理的最大一块内存区域,存放所有对象实例和数组,因为堆存放的对象是线程共享的,所以多线程的时候需要同步机制;堆又划分为:年轻代、老年代、永久代(JDK1.7)/元空间(JDK1.8),元空间与永久代的区别在于:永久代使用的是虚拟机内存,元空间则采用本地内存。

2)虚拟机栈

虚拟机栈描述的是线程进栈出栈的过程,线程结束内存自动释放,它用来存储当前线程运行方法所需要的数据、指令、返回地址(即局部变量和正在调用的方法),方法被调用时会在栈中开辟一块叫栈帧的空间,方法运行在栈帧空间中。

3)本地方法栈

本地方法栈与虚拟机栈的作用十分相似,区别是虚拟机栈执行的是Java方法服务,而本地方法栈则为虚拟机使用 native 方法服务,可能底层调用的 c 或者 c++ 方法。

4)方法区

方法区同堆一样,是所有线程共享的内存区域,又被称为非堆,用于存储已被虚拟机加载的类信息、常量、静态变量等。

5)程序计数器

程序计数器是一块很小的内存空间,它是线程私有的,可以认作是当前线程的行号指示器。

参考:
https://www.jianshu.com/p/8a58d8335270
https://www.jianshu.com/p/de097e7a813a
http://tutorials.jenkov.com/java-concurrency/java-memory-model.html


在这里插入图片描述


猜你喜欢

转载自blog.csdn.net/ityard/article/details/101693717