Java的内存机制(整合)

原文1链接: link.
原文2链接: link.
本文内容均是摘自本文所附上的链接里面的内容,只是整合起来方便查看而已

java的内存结构
java程序的执行流程:
首先java源代码会被java编译器编译成字节码文件,然后JVM的类加载器会加载各个类的字节码文件,加载完毕以后,交给JVM执行引擎执行。在程序执行过程中,JVM会开辟一段空间来存储程序运行所用到的数据和相关信息,这段空间被称作Runtime Data Area(运行时数据区),也被称为JVM内存,平常我们所说的内存管理和内存结构就是针对这段内存而言的。

首先,JVM内存由五个部分组成:虚拟机栈,堆区(Heap),程序计数器,本地方法栈,方法区

1、虚拟机栈
也称java栈,是Java方法执行的内存模型,Java栈中存放的是一个个线帧,每一个线帧对应的是一个方法(如下图所示)
当线程执行一个方法时,会创建一个线帧,并将线帧压栈,方法执行完毕会进行出栈,所以,线程每次运行的方法所对应的线帧一定位于java栈的顶部,(这也是使用递归方法容易内存溢出的原因)
Java的内存机制(整合)_第1张图片局部变量表:存储方法的局部变量,包括在方法中声明的非静态变量和函数形参,对于基本数据类型存的是他的值,对于引用类型的变量存的是它的引用

2、程序计数器
在汇编语言中,程序计数器指的是CPU中的寄存器,它保存的是程序当前执行指令的地址,当CPU需要执行指令时会先拿到这个地址,根据地址获取指令,当CPU获取完指令以后,计数器会自动加一或者转移指针得到下一条命令的地址。
java中的程序计数器的功能和CPU中的寄存器逻辑上的功能差不多,也是用来指示存储哪条命令的。
在JVM中是通过轮流切换线程获取CPU执行时间来实现多线程的,所以在同一时间内,CPU只会执行一个线程,为了保证每个线程在线程切换后能够回到原来的位置,所以每个线程都会有自己独立的程序计数器。
在JVM中规定,如果执行的非native方法,计数器存储的是指令地址,如果是native方法,就是undefined。

3、本地方法栈
与java栈的工作原理相同,不过java栈是为java程序服务的,本地方法栈是为本地方法服务的,用于java调用其他语言的方法。在JVM规范中并没有对本地方法的具体实现做强制规定,JVM可以自由实现它。

4、堆区
存储new出来的东西,即对象本身,堆区是垃圾回收器管理的主要区域。

5、方法区
存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等
在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。

在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。在JVM规范中,没有强制要求方法区必须实现垃圾回收。

总结:
Java的内存机制(整合)_第2张图片

Java的内存模型
原文3链接: link.
并发编程的三个特性:
原子性:是指在一个操作中,CPU 不可以在中途暂停然后再调度,即不被中断操作,要不执行完成,要不就不执行
可见性:是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值
有序性:即程序执行的顺序按照代码的先后顺序执行

为了保证并发编程中可以满足原子性、可见性及有序性。有一个重要的概念,那就是——内存模型。
内存模型解决并发问题主要采用两种方式:

  1. 限制处理器优化
  2. 使用内存屏障

这是一个总结:
JMM 是一种规范,是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。
目的是保证并发编程场景中的原子性、可见性和有序性。

java内存模型的实现:
原子性:使用synchronized
可见性:使用synchronized,final,volatitle(被其修饰的变量在每次使用之前都从主内存刷新)
有序性:使用synchronized,volatitle
(实现方式有所区别:Volatile 关键字会禁止指令重排。Synchronized 关键字保证同一时刻只允许一条线程操作。)

你可能感兴趣的:(java面试准备,java)