Java内存结构和模型

jvm的体系结构分为五个结构。方法区,堆,虚拟机栈,本地方法区,程序计数器PC。其中方法区和堆是所有线程共享的,每一个线程都有一个程序计数器

a、寄存器:这个概念比较模棱两可,网上查了一下一种说法是Java中有寄存器,另一种说法是没有,另外一种说法是没有寄存器,不过寄存器在Java中pc计数器也叫寄存器,在编程思想第二章中明确的将存储数据的地方分为五个地方,分别为寄存器,堆栈,堆,常量存储,非ram存储。

在内存和CPU之间,所有进程都使用寄存器,这是速度最快的存储场所,不过寄存器个数是有限的。在内存中的寄存器区域是由编译器根据需要来分配的。我们程序开发人员不能够通过代码来控制这个寄存器的分配。

    可以看到,在虚拟机栈有一帧帧的 栈帧组成,而栈帧包含局部变量表,操作栈等子项,那么线程在运行的时候,代码在运行        时,是通过程序计数器不断执行下一条指令。真正指令运算等操作时通过控制操作栈的操作数入栈和出栈,将操作数在局部变      量表和操作栈之间转移。

b、本地方法栈:JVM调用了系统中的功能,和虚拟机栈类似,为虚拟机执行java方法(字节码)服务。这个为本地方法服务。方法区,线程共享,用来存放已经被虚拟机加载的类信息,常量,静态变量等。

c、方法和数据共享:运行时期class文件,进行的地方

d、方法栈:也叫静态区所有的方法运行的时期,进行的内存

e、堆(heap):存储的是容器和对象

区域是否线程共享是否会内存溢出
程序计数器不会
java虚拟机栈
本地方法栈
方法区
  1. 堆区: 

    1.存储的全部是对象,每个对象都包含一个与之对应的class的信息(class的目的是得到操作指令) ;2.jvm只有一个堆区(heap),且被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身和数组本身;3,堆得读取速度小于栈,适合存放教大的数据。

  2. 栈区: 

    1.每个线程包含一个栈区,栈中只保存基础数据类型本身和自定义对象的引用;2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问;3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令);4,读取速度很快,仅次于寄存器,但是容量较小。

  3. 方法区(静态区): 

    1.被所有的线程共享,方法区包含所有的class(class是指类的原始代码,要创建一个类的对象,首先要把该类的代码加载到方法区中,并且初始化)和static变量。 2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。其中Java每创建或者首次读取一个Java类,都会创建一个同名.class文件,其中次class对象包含了所有该类的信息(类名。类属性,类变量,修饰符,接口)  

  4. 运行该程序时,首先启动一个Java虚拟机进程,这个进程首先从classpath中找到AppMain.class文件,读取这个文件中的二进制数据,然后把Appmain类的类信息存放到运行时数据区的方法区中,这就是AppMain类的加载过程。 

       接着,Java虚拟机定位到方法区中AppMain类的Main()方法的字节码,开始执行它的指令。这个main()方法的第一条语句就是:

 

运行时常量池,是方法区的一部分,用于保存编译器生成的各种字面量和富豪引用。

1.1. 什么是程序计数器?

程序计数器是一块较小的内存空间,可以把它看作当前线程正在执行的字节码的行号指示器。也就是说,程序计数器里面记录的是当前线程正在执行的那一条字节码指令的地址。 
注:但是,如果当前线程正在执行的是一个本地方法,那么此时程序计数器为空。 
 

1.2. 程序计数器的作用

程序计数器有两个作用:

  1. 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
  2. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。 
     

1.3. 程序计数器的特点

  1. 是一块较小的存储空间
  2. 线程私有。每条线程都有一个程序计数器。
  3. 是唯一一个不会出现OutOfMemoryError的内存区域。

生命周期随着线程的创建而创建,随着线程的结束而死亡。

Java内存模型

Java内存模型看上去和Java内存结构(JVM内存结构)差不多,很多人会误以为两者是一回事儿,这也就导致面试过程中经常答非所为。

在前面的关于JVM的内存结构的图中,我们可以看到,其中Java堆和方法区的区域是多个线程共享的数据区域。也就是说,多个线程可能可以操作保存在堆或者方法区中的同一个数据。这也就是我们常说的“Java的线程间通过共享内存进行通信”。

Java内存模型是根据英文Java Memory Model(JMM)翻译过来的。其实JMM并不像JVM内存结构一样是真实存在的。他只是一个抽象的概念。JSR-133: Java Memory Model and Thread Specification中描述了,JMM是和多线程相关的,他描述了一组规则或规范,这个规范定义了一个线程对共享变量的写入时对另一个线程是可见的。

那么,简单总结下,Java的多线程之间是通过共享内存进行通信的,而由于采用共享内存进行通信,在通信过程中会存在一系列如可见性、原子性、顺序性等问题,而JMM就是围绕着多线程通信以及与其相关的一系列特性而建立的模型。JMM定义了一些语法集,这些语法集映射到Java语言中就是volatile、synchronized等关键字。记住java内存模型是与多线程相关,也叫作共享内存模型,如果被问什么是java内存模型可以这样回答:

Java内存模型简称jmm,它定义了一个线程对另一个线程是可见的,另外就是共享变量的概念,因为Java内存模型又叫做共享内存模型,也就是多个线程会同时访问一个变量,这个变量又叫做共享变量,共享变量是存放在主内存中的,而且每一个线程都有自己的本地私有内存,如果有多个线程同时去访问一个变量的时候,可能出现的情况就是一个线程的本地内存中的数据没有及时刷新到主内存中,从而出现线程的安全问题。
11

接下来我们再来简单的看下这个java内存模型示意图:从这张图我们可以看出,线程之间的通信是受jmm控制的,我们就这张图来说,线程A和线程B如何才能进行通信,假如线程A先执行,它会拿到共享变量,然后进行操作产生共享变量的副本,紧接着将本地内存中的共享变量副本刷新到主内存中,这时共享变量也就得到的更新,同理线程B 再去操作共享变量就达到了线程A和线程B之间的通信了。