前言
其实这块的类容,网上的文章都已经很多很多,以下内容只是综合参考文献以及自己的理解(可能还是错误的)
内存模型(JMM)
常说的java内存模型一般只两种,java运行时数据区与线程内存模型(本地内存与主存)。 前者为java运行时内存分区与存储情况,后者则指的是java线程与内存的交互模型,而这章只讲运行时数据区
运行时数据区
1.8之前:
1.8之后:
虚拟机栈
jvm为每个新创建的线程都分配一个堆栈。堆栈以帧为单位保存线程的状态。jvm对堆栈只进行两种操作:以帧为单位的压栈和出栈操作 每一个栈帧都包括了局部变量表,操作数栈,动态连接,方法返回地址和一些额外的附加信息。在编译代码的时候,栈帧中需要多大的局部变量表,多深的操作数栈 都已经完全确定了,并且写入到了方法表的Code属性中,因此一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体虚拟机的实现
本地方法栈
本地方法栈和虚拟机栈所发挥的作用时非常相似的,它们之间的区别只不过是虚拟机栈为虚拟机执行Java方法服 务,而本地方法栈则为虚拟机使用到Native方法服务,即利用jni调用c/c++。
方法区(元空间)
存放类的元数据(元数据并不是类的Class对象!Class对象是加载的最终产品,类的方法代码,变量名,方法名,访问权限,返回值等等都是在方法区的),同时还存放静态引用(java对象本身永远都不会置入静态存储空间)、JIT缓存代码、运行时常量池等信息
程序计数器
编译后的字节码在没有经过JIT(实时编译器)编译前,是通过字节码解释器进行解释执行。其执行原理为:字节码解释器读取内存中的字节码,按照顺序读取字节码指令,读取一个指令就将其翻译成固定的操作,根据这些操作进行分支,循环,跳转等动作。 从字节码的执行原理来看,单线程的情况下程序计数器是可有可无的。因为即使没有程序计数器的情况下,程序会按照指令顺序执行下去,即使遇到了分支跳转这样的流程也会按照跳转到指定的指令处继续顺序执行下去,是完全能够保证执行顺序的。但是现实中程序往往是多线程协作完成任务的,所以必须有个能记录当前线程的字节码执行位置。对于我们对此没有直接的控制权,也不可能在自己的程序里找到程序计数器存在的任何踪迹
堆
Java的堆是一个运行时数据区,类的对象从中分配空间。这些对象通过new、newarray、 anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。同时全局字符串常量池也在里面。 堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存 大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态 分配内存,存取速度较慢。 在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。 这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。 堆的内存模型大致为: