Java虚拟机的内存模型

  Java虚拟机在执行Java程序的时候,会将它所管理的内存划分为几个区域,这些区域有着各种的用途以及生命周期。有的区域随着虚拟机进程的启动而创建,而有的区域则随着用户线程的启动而建立和销毁。

   虚拟机所管理的内存会分成以下几个运行时数据区:

  

 

  1.程序计数器(Program Counter Register)

  程序计数器所占的内存空间比较小,它表示着当前线程所执行的字节码的行号。

Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,所以,在某个确定的时候,一个处理器(或者说一个内核)都只有一条线程中的指令在执行。因此,为了使线程切换后能够恢复到正确的执行位置,每个线程都需要一个独立的程序计数器,而且各个线程之间的程序计数器互不影响。可以看出,程序计数器是线程私有的

  如果当前线程执行的是Java方法,那么程序计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行Native方法,那么程序计数器的值则为空(Undefined)。此区域不会出现任何OutOfMemoryError。

  2.虚拟机栈(Java Virtual Machine Stack)

  虚拟机栈与程序计数器一样,都是线程私有的,它的生命周期与线程相同。

  虚拟机在执行Java方法时,会创建一个栈帧,用来存储局部变量表、操作数栈、方法出口等信息。每一个方法从调用至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。局部变量表存放着编译期可知的各种基本数据类型、对象引用和returnAddress类型(指向了一条字节码指令的地址)。

  虚拟机栈可能会有两种异常状况:

  ①如果线程请求的栈深度大于虚拟机所允许的深度,那么将会抛出StackOverflowError异常;

  ②如果虚拟机栈可以动态扩展,但是扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

  3.本地方法栈(Native Method Stack)

  本地方法栈与虚拟机栈所起到的作用是非常相似的,两者之间的区别就是,虚拟机栈是为虚拟机执行的Java方法服务的,而本地方法栈则是为虚拟机所使用到的Native方法服务。本地方法栈同样是线程私有的。本地方法栈和虚拟机栈一样,都可能抛出StackOverflowError和OutOfMemoryError异常。

   4.Java堆(Java Heap)

  我们通常会把Java的内存区域比较粗糙的分为堆和栈,其中栈一般是指虚拟机栈,而堆就是指Java堆了。Java堆是被所有线程共享的一块区域,它在虚拟机启动时创建。这块区域的目的就是存放对象,几乎所有的对象实例都是在这里分配内存的,这块区域也是GC的重中之重。

  Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。在实现的时候,既可以实现成固定大小的,也可以实现成可扩展的。当堆被实现成可扩展的的时候,我们可以通过-Xmx(堆的最大空间大小)和-Xms(堆的最小空间大小)来控制。

  Java堆可能发生以下异常情况:

  如果在堆中没有足够的内存去完成实例分配,并且堆也无法再扩展时(比如说,受限于实际的物理内存大小),将会抛出OutOfMemoryError异常。

  5.方法区(Method Area)

  方法区与堆一样,都是属于线程共享的区域。它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码数据。方法区同样是在虚拟机启动的时候创建的。方法区在Java虚拟机规范中被描述为堆的一个逻辑部分,但是它也有一个别名叫非堆(Non-Heap),目的应该是与堆区分开来。

  Java虚拟机规范对方法区的限制其实是非常宽松的,除了和Java堆一样不需要连续的内存和可选择固定内存大小或者可扩展之外,还可以选择不实现垃圾回收。相对而言,垃圾回收的行为在这个区域出现得不多。这个区域的内存回收目标只要是针对常量池的回收以及对类型的卸载。

  方法区和Java堆一样,当它无法满足内存分配的需求时,也会抛出OutOfMemoryError异常。

  6.运行时常量池(Runtime Constant Pool)

    运行时常量池其实是方法区里面的一部分。Class文件中除了有类的版本、字段、方法、接口等信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用。这部分内容将在类加载后进入方法区的运行时常量池中存放。

  运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性。Java语言并不要求常量一定只有在编译期才能产生,也就是说,并非预置入Class文件中常量池的内容才能进入方法区的运行时常量池,程序在运行期间产生的新的常量也可以放入池中(比如用String.intern()方法)。

  运行时常量池既然是方法区中的一部分,那么它也无可避免的收到方法区内存的限制。当常量池无法再申请到内存时,也会抛出OutOfMemoryError异常。

原文地址:https://www.cnblogs.com/WakingShaw/p/15739654.html