JMM与JVM区别与联系(精炼总结)

在多线程编程中,就涉及到线程之间的通信。为了更好的实现程序的高并发、高性能、高可用,就不得不知道JMM。至于高可用,今后再详细总结。另外,此处有一文介绍HA,参考https://www.linuxprobe.com/high-availability.html


一、JMM

1、JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是java整个计算虚拟模型。

2、从抽象的角度来看,JMM定义了线程主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。

3、本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。

JMM与JVM区别与联系(精炼总结)_第1张图片

二、JVM对Java内存模型的实现

根据JMM模型,在JVM把内存分成了两部分:线程栈区堆区

JVM中运行的每个线程都拥有自己的线程栈(也称调用栈),线程栈包含了当前线程执行的方法调用相关信息。随着代码的不断执行,调用栈会不断变化。

JMM与JVM区别与联系(精炼总结)_第2张图片
1、线程栈区(Thread stack)

1)线程栈(thread stack,也叫线程堆栈),所拥有的资源是独自的,对其他线程不可见。

2)线程栈,也包含正在的所有局部变量。

3)由当前线程创建的局部变量,对于非创建它的其他所有线程都是不可见的。

4)即使两个线程正在执行完全相同的代码,两个线程仍然会在每个线程堆栈中创建该代码的局部变量。即便可传递变量副本,但不共享原始局部变量本身。

2、堆区(Heap)

1)堆包含创建的java应用程序对象。

2)堆中的对象可以被具有对象引用的所有线程访问。当一个线程访问一个对象时,它也可以访问该对象的成员变量。

3)如果两个线程同时调用同一个对象上的一个方法,它们都可以访问该对象的成员变量,但每个线程都有自己的局部变量副本

4)堆中的数据是共享的,线程不安全的

详细说明:

  • 所有原始类型(boolean,byte,short,char,int,long,float,double)的局部变量都直接保存在线程栈当中,对于它们的值各个线程之间都是独立的。对于原始类型的局部变量,一个线程可以传递一个变量副本给另一个线程,但原始变量是不共享的。
  • 堆区包含了Java应用创建的所有对象信息(包括原始类型的封装类),不管对象是哪个线程创建的,不管对象是属于一个成员变量还是方法中的局部变量,它都会被存储在堆区。
  • 一个局部变量如果是原始类型,那么它会被完全存储到栈区。 一个局部变量也有可能是一个对象的引用,这种情况下,这个本地引用会被存储到栈中,但是对象本身仍然存储在堆区。
  • 对于一个对象的成员方法,这些方法中包含局部变量,仍需要存储在栈区,即使它们所属的对象在堆区。
  • 对于一个对象的成员变量,不管它是原始类型还是包装类型,都会被存储到堆区。
  • Static类型的变量以及类本身相关信息都会随着类本身存储在堆区。

JMM与JVM区别与联系(精炼总结)_第3张图片


基于JMM的JVM模型,既然堆中的数据是共享的,那么在多线程环境中,就可能存在数据安全性问题。主要涉及到:可见性问题,竞争性问题等。

在此之前,回顾几个点

A、计算机常识:

  • cpu执行的操作是原子性的,是不可拆分的。

B、造成数据安全性问题的必要条件

  • 多线程环境
  • 多个线程操作共享数据
  • 操作共享数据的语句不是原子性的(多条)

此中,引申的。后续补充


3、共享对象的可见性(事例说明)

如果两个或多个线程共享一个对象,但没有正确使用volatile声明或Synchronized同步机制,一个线程更新了共享变量值后,对于其他线程来讲是不可见的。如线程A,线程B同时要进行modify。

public class Account {

  private float balance;

  public void modify (float difference) {
    float value=this.balance;
    this.balance=value+difference;
  }
}

首先,线程A和线程B在各自的thread stack中维护了一分局部变量的副本,线程A修改了线程A中Thread stack中的局部变量,但是还没有还没将修改的数据刷新到Main Memory中,而线程B获取的值依然是old value,就会出现问题

解决方案

  • 使用volatile关键字
  • 使用synchronized同步机制

tips:volatile与synchronized的区别:

  • volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住
  • volatile仅修饰变量;synchronized则可以修饰变量、方法、代码块
  • volatile仅保证可见性;synchronized则可以保证可见性和原子性
  • volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞
  • volatile修饰的变量会禁止指令重排序,因而程序不会被编译器优化;synchronized修饰的变量没有禁止指令重排序,因而程序可以被编译器优化

三、JVM

  • 程序计数器(PC)
  • java虚拟机栈
  • 本地方法栈
  • java堆
  • 方法区

1、程序计数器(PC)

是一块很小的内存空间,用于记录将要运行的指令。

tips:每个线程都需要一个程序计数器;各个线程的计数器相互独立,是私有的。

2、java虚拟机栈

保存了局部变量、部分结果,并参与方法的调用和返回,的一个内存空间

tips:1)对各个线程来说,也是私有的;

         2)它和java线程同一时间创建;

         3)由java语言实现的

3、本地方法栈

与java虚拟机栈的功能相似

tips:1)java虚拟机栈用于管理Java函数的调用,本地方法栈用于管理本地方法的调用;

         2)由C语言实现的

4、java堆

tips:1)做存储的,为所有创建的对象和数组分配内存空间;

         2)被JVM中所有的线程共享

5、方法区

也被称为永久区,与堆空间相似,被JVM中所有的线程共享。

         1)方法区主要保存的信息是类的元数据,方法区中最为重要的是类的类型信息、常量池、域信息、方法信息。其中运行时常量池就在方法区;

         2)GC对永久区的回收:一是对永久区常量池的回收;二是永久区对元数据的回收


参考鸣谢:

https://blog.csdn.net/u013887008/article/details/79681609 

https://www.jianshu.com/p/8a58d8335270

你可能感兴趣的:(java夯实基础)