Jvm 内存模型
PC 寄存器(程序计数器)
- 程序计数器是一块较小的内存空间,可以把它看作当前线程正在执行的字节码的行号指示器。
- 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
- 特点:较小的存储空间;线程私有;随线程创建而创建,结束而死亡。
Java虚拟机栈
- Java虚拟机栈会为每一个即将运行的Java方法创建一块叫做“栈帧”的区域,这块区域用于存储该方法在运行过程中所需要的一些信息,这些信息包括:
- 局部变量表,存放基本数据类型变量,引用类型变量,returnAddress 类型变量。
- 操作数栈
- 动态链接
- 方法出口信息等
- 方法执行完毕,就会释放空间。
- 线程私有。
- 栈中的数据仅限于基本类型和对象引用。所以,在JVM中,栈上是无法保存真实的对象的,只能保存对象的引用。真正的对象要保存在堆中
本地方法栈
和 “Java虚拟机栈” 类似,只是为“本地方法”服务的而已。
堆
- 堆事用来存放对象的内存空间。
- 几乎所有的对象都存储在堆中;垃圾回收的主要场所。
- 线程共享,整个Java虚拟机只有一个堆。
- 堆上也无法保存基本类型和对象引用。堆和栈分工明确。但是,对象的引用其实也是对象的一部分。
- 数组是保存在堆上面的,即使是基本类型的数据,也是保存在堆中的。因为在Java中,数组是对象。
方法区
- 方法区市堆的一个逻辑部分。
- 存放已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等。
- 线程共享
- 永久代 — 方法区中的信息一般需要长期存在。
运行时常量池
- 放三种数据:类信息、常量、静态变量、即时编译器编译后的代码。
JVM 内存模型小结
- 一共2个栈,功能类似,线程私有,都是方法运行过程的内存模型。
- 一共2个堆,一个原本的“堆”,一个方法区。
- 堆是 Java虚拟机中最大的一块内存区域,也是垃圾收集器的主要工作区域。
Java 内存模型
主要解决并发编程时的原子性、有序性、一致性的问题。在Java中有一系列封装好的关键字比如volatile
,synchronized
,final
,concurren
。
原子性:
sychronized
保证方法和代码块内的操作是原子性的。
可见性:
Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值的方式作为传递媒介来实现的。
有序性:
volatile 防止指令重排,保证有序性。
synchronized:
- 线程解锁前,必须把共享变量的最新值刷新到主内存中;
- 线程加锁时,将清空工作内存中的共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值。
- 所以,synchronized 不仅能保证原子性,还能保证可见性。
- 缺点:(可以使用 ReentrantLock 来解决)
- 不能中断一个正在试图获得锁的线程;
- 不能设置超时时间,如果发生了死循环,锁就永远不能释放了;
- 每个锁都只有单一的条件。
volatile:
- 被volatile修饰的变量在修改后立即同步到主内存,每次用之前都从主内存刷新。
- 禁止指令重排。
- 所以,volatile 不能保证原子性。
Java 对象模型
每一个Java类,在被JVM加载的时候,JVM会给这个类创建一个instanceKlass
,保存在方法区,用来在JVM层表示该Java类。当我们在Java代码中,使用new创建一个对象的时候,JVM会创建一个instanceOopDesc
对象,这个对象中包含了两部分信息,对象头以及元数据。对象头中有一些运行时数据,其中就包括和多线程相关的锁的信息。元数据其实维护的是指针,指向的是对象所属的类的instanceKlass
。