0%

内存模型和内存结构

之前有点傻傻的分不清内存模型和内存结构的区别,以为都是指的是JVM。直到前段时间在一篇博客上看到这两者的区别,才知道这两者指的是不同的东西。

内存结构

内存结构就是我们常说的JVM,比如,堆、栈等。这些就是Java的内存结构。

关于JVM可以看 深入理解Java虚拟机(周志明) 写的这本书。下面两篇博客是我看这本书的一些笔记:

内存模型

Java内存模型(即Java Memory Model,简称JMM)本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。Java虚拟机定义JMM来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数据,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,工作内存中存储着主内存中的变量副本拷贝,前面说过,工作内存是每个线程的私有数据区域,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下图:

简单的总结,Java多线程之间通过共享内存来进行通信,但由于采用共享通信,在通信过程中会出现可见性、原子性、顺序性等问题,而JMM就是围绕多线程通信以及其相关的一系列特性建立的模型。

内存屏障(Memory Barrier)

内存屏障,又称内存栅栏,内存栅障,屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。

  • 保证特定操作的执行顺序。
  • 影响某些数据(或则是某条指令的执行结果)的内存可见性。

happens-before

happens-before原则定义如下:

  • 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
  • 两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。

happens-before规则如下:

  • 程序顺序规则:一个线程内,安装代码顺序,书写在前面的操作先行发生于书写后面的操作,happens-before于该线程中任意的后续操作;
  • 监视器锁规则:对一个锁的解锁操作,happens-before于随后对这个锁的加锁操作;
  • volatile域规则:对一个volatile域的写操作,happens-before于任意线程后续对这个volatile域的读;
  • 传递性规则:如果 A happens-before B,且 B happens-before C,那么 A happens-before C;
  • 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;
  • 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
  • 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;
  • 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始。

指令重排序

在执行程序时,为了提高性能,编译器和处理器会对指令做重排序。但是,JMM确保在不同的编译器和不同的处理器平台之上,通过插入特定类型的Memory Barrier来禁止特定类型的编译器重排序和处理器重排序,为上层提供一致的内存可见性保证。

Reference

客官,赏一杯coffee嘛~~~~