深入理解Java虚拟机(一)java运行时数据区域

Java语言和虚拟机规范官方文档:https://docs.oracle.com/javase/specs/index.html

概述

Java虚拟机在执行程序的过程中,会把它管理的内存分为不同的数据区域。其中一些数据区域是在Java虚拟机启动时创建的,仅在Java虚拟机退出时才被销毁。有些数据区域是每个线程私有的。在创建线程时创建每个线程的数据区域,并在线程退出时销毁每个数据区域。如图所示:


image.png

程序计数器

程序计数器可以看作当前线程所执行的字节码的行号指示器。

  • 每个线程都需要一个独立的程序计数器,各个线程之间的计数器互不影响独立存储,可以说这类内存区域是线程私有的。

  • 如果线程正在执行的是Java方法,那么这个计数器记录的是正在执行的虚拟机字节码指令的地址。

  • 如果线程正在执行的是Native方法,那么这个计数器的值是Undefined。

  • 此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutofMemoryError情况的区域。

Java虚拟机栈

Java虚拟机栈也是线程私有的,它是为虚拟机执行Java方法(也就是字节码)服务。

  • 每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

  • 每个方法从调用到执行完成过程中,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

  • 局部变量表存放了编译期可知的8种数据类型、对象引用和returnAddress类型(指向了一条字节码指令地址)。

  • 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。

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

本地方法栈

本地方法栈是为虚拟机适用到的Native方法服务的。有的虚拟机(譬如sun HotSpot)直接就把本地方法栈和虚拟机栈合二为一。它抛出的异常与虚拟机栈类似。

Java堆

对大多数应用来说,Java堆是虚拟机管理的内存中最大的一块区域,是被所有线程共享的区域。官方文档中写道


The heap is the run-time data area from which memory for all class instances and arrays is allocated.

即所有的对象实例和数组都要在堆上分配。但随着JIT编译器发展,这并非绝对的。

  • Java堆书垃圾回收的主要区域。它还可以细分为:新生代和老年代;再细致一点有Eden空间、From Survivor空间、To Survivor空间。

  • 如果在堆中没有内存可用,且堆无法再扩展时,就会抛出OutOfMemoryError异常。

方法区

方法区也是各个线程共享的内存区域,用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

官方文档中只规定了方法区这个概念,但没有规定具体的实现方式。在JDK1.7以前,HotSpot虚拟机主要使用永久代的方式来实现方法区,JDK1.8开始改为元空间。

运行时常量池

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

运行时常量池相比于Class文件常量池,具备动态性。Java并不要求常量一定只有编译期才能产生,运行期间也可能有新的常量放入池中。例如String类的intern()方法。

关于String类的intern方法,推荐一篇文章:https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html

你可能感兴趣的:(深入理解Java虚拟机(一)java运行时数据区域)