JVM虚拟机内存模型

1.说jvm的内存模型前先了解一下物理计算机的内存处理

物理计算器上用户磁盘和cpu的交互,由于cpu读写速度速度远远大于磁盘的读写速度速度,所以有了内存(高速缓存区)。但是随着cpu的发展,内存的读写也跟不上cpu的读写速度了,cpu的产商就给每个cpu加入了一个高速缓存,也就是下面的结构。

JVM虚拟机内存模型

每个cpu都有自己的高速缓存,在多核处理环境下,就会出现内存一致性问题,所以在运行时制定了一系列的协议(MSI、MESI、MOSI、Synapse、Firely、DragonProtocol)来保证缓存一致性。 

2.jvm虚拟内存处理同物理计算机的内存处理很相似,下面盗用一张图片,原出处:深入理解JVM—JVM内存模型

JVM虚拟机内存模型

多线程运行的时候,每个线程都会有自己的一块工作内存,所有线程共享主存。当线程与主存交互时,数据从主存拷贝到工作内存区域,数据操作完成后再重新写回主存,在这也有内存一致性问题。如果有某一个线程再对主存中变量做写操作,那么会清空该变量在其他线程在工作线程中的副本,直接读取主存中的值,来保证数据的一致性,遵循happens-before原则,写操作发生在读之前。

JVM内存模型

JVM虚拟机内存模型

从上面的图可以分析出  pc计数器、jvm栈、native方法栈是每个线程私有的,堆、方法区、运行常量池时所有线程共享的。

是java虚拟机所管理的内存中最大的一块存储区域,该内存 存放对象实例及数组。

大小可以通过-Xms(最小值)和-Xmx最大值来设置(不能超过1G)

-Xms:启动是申请的最小内存,默认操作系统的1/64

-Xmx:JVM可申请的最大内存,默认为物理内存的1/4

-XX:MinHeapFreeRation=设置最小空余堆比例,默认是40%,当空余小于百分之四十时,JVM会增加到-Xms指定的大小,为避免运行时频繁的调整Heap大小,通常将-Xms和-Xmx设置成一样的大小。

垃圾回收的重点区域。

方法区

,用于存储虚拟机加载的类的信息(类的名字、方法信息、字段信息)、常量、静态变量以及编译器编译后的代码,是各个线程共享的内存区域。方法区也可以是内存不连续的区域组成,永久代和老年代是是捆绑在一起的,无论谁满了都会触发永久代和老年代垃圾收集。

-XX:PermSize:默认最小值为16MB

-XX:MaxPermSize:最大值为64MB(64位指针膨胀,默认是85M)。

当JVM类加载信息超过XX:MaxPermSize设置的值时,会报OOM错误

运行常量池:是方法区的一部分,class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,存放编译器生成的各种符号引用,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。在运行时也可以将新的常量放入运行时的常量池中,比如String和intern方法。

在jvm中没有强制要求必须实现垃圾回收,HotSpot虚拟机是一以永久代来实现方法区的,垃圾回收器可以像管理堆一样管理这部分区域,主要回收的目标的是对类型的卸载和常量池的回收。

废弃常量:常量池中的某个常量没有被任何引用所引用即为废弃常量。

无用类: 1.该类的所有实例都已经被回收,java堆中不存在该类的实例对象

                2.加载该类的加载器已经被回收

                3.该类锁对应的java.lang.Class是对象没有任何地方被引用,无法在任何地方通过反射机制访问该类的方法。

jdk7以后,存储在永久代中的一部分数据转移到了java Heap或者Native Heap;符号引用转移到native heap ,字面量转移、类的静态变量转移到java heap;

jdk8开始 使用元空间,元空间的大小受本地内存限制,MaxMetaspaceSize用于限制本地内存分配给类元数据的大小,如果没有指定这个参数,元空间会在运行时根据需要动态调整。

虚拟机栈

java方法执行的内存模型:每个方法执行时会创建一个栈帧,方法的执行就是一个栈帧入栈出栈的过程。

栈帧:局部变量区、操作数栈、栈数据区。

局部变量区:以一个字长为单位的数组,对于基本数据类型,直接存储它的值,对于引用类型变量,则存的是指向对象的引用,局部变量表的大小在编译器就可以确定,因此在程序执行期间局部变量表的大小是不会改变的。

操作数栈:也是以一个字长为单位的数组。但不是通过下标索引来访问的,而是通过入栈出栈来访问的,临时数据存储区域,实际上一个线程执行方法的过程就是进行计算的过程,程序中所有计算过程都是借助操作数栈来完成的。

栈数据区:存储一些支持常量池解析、正常方法返回急异常派发等机制的数据。如下。

指向运行时常量池的引用,在方法执行过程中可能会用到类中的常量,所以必须要有一个引用指向运行时的常量。

方法返回地址,当一个方法执行完毕后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。

如果线程请求的栈深度大于虚拟机允许的深度

本地方法栈

和虚拟机栈类似,区别在于虚拟机栈为java方法服务,而本地方法栈则为Native方法服务。

程序计数器

最小的一块内存区域,作用是当前线程所执行的字节码的行号指示器,在虚拟机的模型中字节码解释器的工作就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、异常处理、线程恢复等基础功能都需要依赖计数器完成。如果执行的是native方法。计数器的值为空。这个区域是jvm规范中唯一没有规定OOM的区域。

 

 

参考博客:JVM内存模型总结

                  深入理解java虚拟机栈