您的位置:首页 > 编程语言 > Java开发

Java内存区域

2016-11-02 16:21 176 查看
  Java虚拟机在执行Java程序的过程中会把它所管理的内存区域划分为若干个不同的数据区域,称为运行时数据区域,包括程序计数器Java虚拟机栈本地方法栈Java堆方法区,如下图所示。

  

  


  

  

程序计数器:

  程序计数器是一块较小的内存空间,可以被看作是当前线程所执行的字节码的行号指示器,分支、循环、跳转、异常处理和线程恢复等基础功能都需要依赖这个计算器来完成。由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线成中的指令。因为,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储(我们称这类内存区域为线程私有的内存,反之称为线程共享的内存,上图说明了运行时内存区域中哪些是线程私有的区域,哪些是线程共享的区域)。

  由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。

  

Java虚拟机栈:

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

  局部变量表,顾名思义,就是用来存储方法中的局部变量的,它存放着编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double、对象引用以及returnAddress)。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法所需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

  对这个区域,JVM规范规定了两种异常状况:

1、StackOverflowError异常:线程请求的栈深度大于虚拟机所允许的深度;

2、OutOfMemoryError异常:在虚拟机栈可以动态扩展的情况下,如果扩展时无法申请到足够的内存。

本地方法栈:

  本地方法栈与Java虚拟机栈作用非常相似,区别仅在于虚拟机栈为Java方法(也就是字节码)服务,而本地方法则为Native方法服务。

  

Java堆:

  Java堆在大多数应用中是JVM所管理的内存中最大的一块区域,此内存区域的唯一目的就是存放对象实例。对于可扩展的JVM实现,如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

  

方法区:

  方法区用来存储已被虚拟机加载的类信息(Class文件)、常量(final)、静态变量(static)、即时编译器编译后的代码等数据。

  运行时常量池:运行时常量池是方法区的一部分。Class文件中有一项信息叫做常量池,其中存放着编译期生成的各种字面量和符号引用,当类加载后,Class文件中常量池的内容就进入方法区的运行时常量池中存放,除此之外,由符号引用翻译出的直接引用也会存储在运行时常量池。

  当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

  

补充:

  在上述五个内存区域中,程序计数器、虚拟机栈和本地方法栈这3个区域随线程而生,随线程而灭,方法结束或者线程结束时,内存自然就跟着回收了,所以这几个区域的内存分配和回收都具备确定性,不需要过多地考虑回收的问题。而Java堆和方法区则不一样,它们是线程共享的内存区域,我们只有在程序运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的也都是这部分内存。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 内存 虚拟机