JVM的内存模型分为下面五个部分: 虚拟机栈(为每个线程创建栈帧),本地方法区(Native修饰的三方语言方法),方法区,堆,程序计数器
JVM线程私有的区域.它代表的java方法执行的JMM内存计算模型(所有的线程执行都要从主线程的共享内存中拿共享变量,到自己的内存中使用,然后再写入到主内存中),每个方法的执行都会同时创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息.方法执行完后,会有一个出栈的过程带着程序的返回值,并清除掉这个栈帧(这里就很好解释关于参数传递的面试题),并且虚拟机栈会有Stack OverflowError和OOM
局部变量表存放了编译器可知的各种基本数据类型和引用类型的指针.
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序被编译成Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需要分配的.
操作数栈 Java虚拟机的解释执行引擎被称为"基于栈的执行引擎",其中所指的栈就是指-操作数栈。操作数栈也常被称为操作栈。 和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。虚拟机在操作数栈中存储数据的方式和在局部变量区中是一样的:如int、long、float、double,reference和returnType的存储。对于byte、short以及char类型的值在压入到操作数栈之前,也会被转换为int。
虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。比如,iadd指令就要从操作数栈中弹出两个整数,执行加法运算,其结果又压回到操作数栈中,看看下面的示例,它演示了虚拟机是如何把两个int类型的局部变量相加,再把结果保存到第三个局部变量的:
可以看出java调用本地方法栈是需要和第三方语言接口方法进行交互的,因为native方法java底层是不能访问的,需要底层调用.
对于大多数的应用来说,堆就是一块物理内存区域,存放所有的共享数据,所有的实例,数组实现都要存放在heap中,Java堆可使用-Xms -Xmx进行内存控制,值得一提的是从JDK1.7版本之后,运行时常量池从方法区移到了堆上。堆还是javaGC发生的主要区域:新生代和老年代,新生代:中细分为Eden和survivor0,survivor1 比例8:1:1Eden是对象存活时间最短的一部分,大部分的方法操作中都会在这里存放,然后方法用完立即回收.survivor0存放Eden仍然存活的对象,survivor1存放survivor0中的对象.老年代就是存放经过多次GC后还存活的对象,基本就是survivor1 中的.
如果在堆中没有完成对象的分配,也没有额外扩展时,就会发生StackOverFlowError和OOM
方法区和堆一样,也是各个线程的共享区域.存储已被JVM加载的类信息,常量,静态变量,即时编译器编译后的代码等数据.也就是用来存储描述类信息的.GC在这个区域是非常少的,所有1.7之前称之为永久代.当方法区的内存无法满足分配需求时,就会抛出OOM:MetaSpace error.
(在jdk1.8 将永久代彻底删除,用MetaSpace代替称为元空间))
运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池( Constant pool table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入运行时常量池中存放。运行时常量池相对于class文件常量池的另外一个特性是具备动态性,java语言并不要求常量一定只有编译器才产生,也就是并非预置入class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中。
在近三个JDK版本(1.6、1.7、1.8)中, 运行时常量池(Runtime Constant Pool)的所处区域一直在不断的变化,在JDK1.6时它是方法区的一部分;1.7又把他放到了堆内存中;1.8之后出现了元空间,它又回到了方法区。其实,这也说明了官方对“永久代”的优化从1.7就已经开始了。
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是JVM规范中定义的内存区域。但这部分内存也被频繁的使用,而且也可能导致OutOfMemoryError异常出现。它在JDK中最直观的表现就是NIO,基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作(说白了就是不属于JVM层面的内存,是物理内存)