JVM内存区域的划分

1.概述

   JVM内存区域简单的可以划分为java堆和java栈,详细的可以划分为方法区,堆,程序计数器,虚拟机栈和本地方法栈。

  JVM内存区域的划分

     其中方法区和堆是所有线程共享的,本地方法栈,虚拟机栈和程序计数器是线程独占的。

2.详细介绍

    2.1程序计数器

        程序计数器相当于当前线程所要执行字节码的行号指示器,字节码解释器通过改变行号指示器的值来获取下一条要执行的字节码指令,当执行本地方法时这个程序计数器的值为空,这也是Java虚拟机规范中没有规定任何OutofMemoryError情况的区域。  

     程序计数器每个线程独享可以避免在多线程的情况下线程切换时每次都能恢复到正确的位置执行。

    2.2虚拟机栈

       虚拟机栈描述的是java方法执行的内存模型,每个方法在执行时都会创建一个栈桢用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从执行到调用完成的过程就对应着一个栈桢在虚拟机栈中入栈到出栈的过程。

    虚拟机栈的局部变量表存储了编译器克制的各种基本数据类型和对象引用(reference类型,指向对象起始地址的引用指针),其中64位长度的long和double类型的数据会占用2个局部变量空间,其余数据类型占据一个。局部变量表所需的内存空间在编译时期就已经完成分配固定,在运行期间不会改变局部变量表的大小。

    可能会出现的两种错误:

异常名 错误原因
*Error 线程所请求的栈深度
OutofMemoryError 如果虚拟机栈可以动态扩展,扩展时无法申请到足够的内存

以操作数栈为例讲解压栈和出栈的过程:

public static int add(int a,int b){

    int c=0;

    c=a+b;

   return c;

}

0:   iconst_0 // 0压栈

1:   istore_2 // 弹出int,存放于局部变量2

2:   iload_0  // 把局部变量0压栈

3:   iload_1 // 局部变量1压栈

4:   iadd      //弹出2个变量,求和,结果压栈

5:   istore_2 //弹出结果,放于局部变量2

6:   iload_2  //局部变量2压栈

7:   ireturn   //返回

JVM内存区域的划分

    2.3本地方法栈

    和虚拟机栈发挥的作用非常相似,区别是虚拟机栈执行的是Java方法,而本地方法栈指向的是Native方法。在某些虚拟机中会把本地方法栈和虚拟机栈合二为一(例如Sun HostSpot)。

    2.4Java堆

    Java堆(Java Heap)是所有线程共享的一块数据区域,也是垃圾收集器管理的主要区域,因此很多时候也被成为GC堆.在虚拟机启动时创建,此内存区域存在的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。Java堆可以处于物理上个不连续的内存空间中,只要逻辑上连续就可以。在实现是可以固定大小,也可以可扩展,不过当前主流的虚拟机都是按照可扩展实现的,。如果堆中没有内存完成实例分配,并且堆也无法扩展时,将会抛出OutOfMemoryError异常。

   常用参数:

  

参数名 作用
-Xmx 最大可用内存
-Xms 初始Jvm内存大小
-Xmm 新生代大小,3/8内存大小
-Xss 每个线程的堆栈大小,3000-5000

    2.5方法区

     与Java堆一样也是各个线程共享的一个数据区域,用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范吧方法区描述为堆的一个逻辑部分,但是他还有一个别名叫做非堆(Non-Heap)。

   在HotSpot虚拟中,把GC分代扩展至方法区,用永久代来实现方法区,使HotSpot可以像管理Java堆一样来管理这部分内存,省去了专门管理方法区的代码工作。但是容易产生内存溢出的问题,规划用Native Memory来替换永久代改善内存溢出的问题。在JDK1.7的HotSpot中已经把原本放在永久代中的字符串常量池中移出。