1 堆

        Java堆是所有线程所共享的一块内存,在虚拟机启动时创建,几乎所有的对象实例都在这里创建,因此该区域经常发生垃圾回收操作。需要留意的是,并不是所有的对象都是分配在堆中,后面会说明。


2 方法区

线程共享的。方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。

        Java 8之前,方法区的实现是永久代。但是从Java 8开始,HotSpot虚拟机中已经没有永久代了,取而代之的是元空间(Metaspace)。元空间和永久代类似,都是对JVM规范中方法区的一种实现。类的元数据被放入到元空间中,而字符串常量和类的静态变量则被移动至Java堆中。这样做可以使加载多少类的元数据都不由MaxPermSize控制,而由系统的实际可用空间来控制。减少OOM的产生,并且为HotSpot和JRockit虚拟机的融合做出努力(两者合并的结果即“HotRockit”项目只能说差强人意。HotSpot除了用本地内存代替永久代实现方法区之外,包括JMC、JFR和NMT等功能都是从JRockit借鉴过来的。其他功能由于两者架构的差异性明显,HotSpot能够直接借鉴融合的功能寥寥无几)。


3 虚拟机栈

局部变量表操作数栈动态链接方法出口等信息。虚拟机栈是线程私有的,它的生命周期与线程相同。

        局部变量表里存储的是基本数据类型、returnAddress类型(指向一条字节码指令的地址)和对象引用,这个对象引用有可能是指向对象起始地址的一个指针,也有可能是代表对象的句柄或者与对象相关联的位置。局部变量所需的内存空间在编译期间确定。

        操作数栈的作用主要用来存储运算结果以及运算的操作数,它不同于局部变量表通过索引来访问,而是压栈和出栈的方式。

        每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接。动态链接就是将常量池中的符号引用在运行期转化为直接引用,将main方法以及静态方法替换成指向数据所存内存的指针。

        方法出口指的是保存方法跳出的代码点,该方法里面执行到子方法时,会再开辟一个子方法的栈帧,等子方法执行完毕、销毁这个栈帧后,需要再跳回到父方法跳出的代码点,这时方法出口就记录了这个跳出点。

        而JVM的运行模式有三种:

  • 解释模式:执行一行字节码就编译为一行机器码;
  • 编译模式:先将整个字节码文件全部编译成机器码,然后一次性执行;
  • 混合模式:大体上使用解释模式来执行,但对于一些热点代码采取编译模式来执行,这也是默认的运行模式。

        混合模式兼顾了解释模式启动速度快的优点,以及编译模式后期运行速度快的特点,因为一些热点代码所对应的机器码会被缓存起来,下次再执行的时候无需再编译,也就是JIT即时编译技术

有可能这些对象不会在堆中被分配内存,而是在栈中,以此来分担堆分配内存的压力。但这也不是绝对会发生的。


4 本地方法栈

        本地方法栈和虚拟机栈类似,只不过本地方法栈执行的都是native方法。


5 程序计数器 

        内存空间小,字节码解释器工作时通过改变这个计数值可以选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数器完成。该内存区域是唯一一个Java虚拟机规范没有规定任何OOM情况发生的区域。