JVM和JMM

jvm
jvm内存结构是java虚拟机内部的内存划分方案。
JVM和JMM_第1张图片
1)从图中可以看出 classloader类加载器,就是将class文件加载到内存,就是把描述类的元数据 以及信息加载到内存 并进行校验,初始化 并最终形成可被虚拟机使用的java类型,这就是java类加载器的作用
2)RUNTIME AREA(运行时数据区) 就是我们常说的虚拟机管理的内存模型了。所写的程序都被加载到这里,之后才开始运行。
3)方法区,存放加载的类信息,常量,静态变量 static 也包括即时编译的信息和利用反射创建的class信息,该部分能够实现线程共享,
a)运行时常量池,在方法区中,每个类型都对应一个常量池存放该类型所用的所有常量,常量池中存储了诸如,常量字符串,flnal变量值,类名及方法名常量,它们以数组形式存储并通过索引被访问,是外部调用与类联系及类型对象化的桥梁,(存的可能是个普通字符串,然后通过常量池解析 最后变成指向某个类的引用)
b),字段信息,字段信息存放类中声明的每一个字段的信息,包括字段的名 类型 修饰符 字段名称指的是类或接口的实例变量或类变量,字段的描述符是一个指定字段类型的字符串
c)方法信息,类中声明的每一个方法的信息,包括方法名,返回值类型,参数类型,修饰符 异常,方法的字节码,(编译的时候,就已经将方法的局部变量,操作数栈大小确定并存放在class字节码中,在装载的时候,随着类一起装入方法区。)
d)静态变量,就是类变量,类的所有实例都共享,在方法去中有一个静态区,静态区专门存放静态变量和静态块
e)jvm为每个加载的类型(包括类和接口)都创建一个java.lang.class的实例,而jvm必须以某种方式把class的这个实例和存储在方法区中的内存数据联系起来。
4)java栈,java虚拟机栈,线程私有 生命周期和线程生命周期相同,每个方法被执行的时候都会创建一个栈帧,用于存储局部变量表,操作栈 动态链接,方法出口
5)本地方法栈,用于存储为虚拟机使用的内部方法
6) 堆,线程共享的内存区域,也是最重要的内存区域,存放我们对象的实例,GC垃圾回收器主要在这里工作,基本采用的是分代收集算法,细分为新生代和老年代,更细致的一点又Eden空间 from survivor to survivor 空间 所有的对象实例和数组都要在堆中分配
7)PC程序计数器,计数器呢 是存放java线程的私有数据的,这个数据存放的是下一条指令的地址,对于我们的方法来说也是私有的
JMM
JVM和JMM_第2张图片
JMM中的主内存与jvm中的Java堆,栈,方法区等 并不是同一个层次的内存划分,基本上是没有关系的,勉强对应起来 变量 主内存 工作内存 从定义上来看 主内存主要对应于java堆中的示例数据部分,而工作内存则对应于虚拟机栈的部分区域,从更低层次来说,主内存就直接对应物理硬件的内存,而为了获取更好的运行速度,虚拟机(甚至是硬件系统本身的优化措施)可能会让工作内存优先存储于寄存器和高速缓存器中,因为程序运行过程中主要读写访问的是工作内存

线程在运行过程当中,首先会从工作空间中读取数据,如果没有找到,则会从主内存中获取,放入工作区(数据复制) 进行修改 修改完成后 将数据刷新到内存当中 其目的是为了保证数据的不可见性从而保证一致性,从而给每一个工作空间分配的大小是根据栈帧的大小来分配的,vmstack以及pc程序计数器是在工作空间里面去分的

硬件结构
JVM和JMM_第3张图片
由于cpu执行速度快 而内存读写速度慢,所以在cpu和内存中间 会加入各级缓冲(cache)提高性能 在编译器的编译重排序 和运行期的指令重排序,现在处理器基本上是双核以上cpu,在并发处理的过程中,会造成不同步的问题(缓存的一致性)
解决方案
a,总线加锁(BUS)将总线控制起来,同一时间,只能有一个cpu占用总线,加锁力度大,但这样大大降低了性能 降低了cpu的吞吐量
b,缓存一致性协议(MESI),当cpu在缓存当中操作数据时,如果该数据是共享变量,则cpu会将数据从CACHE中读取到寄存器当中,那么此时将会把CHACHE limit标记为无效,那么其他cpu则会直接从内存当中去读取数据,这样来保证数据的一致性
JMM模型和cpu架构模型之间的交互会产生数据的不一致性问题
JVM和JMM_第4张图片
java内存模型的必要性 为了规范内存数据和工作空间数据交互带来的不一致性问题 (并发编程 原子 可见 有序)

你可能感兴趣的:(内存模型)