image.png

程序计数器

当前线程执行字节码的行号指示器

虚拟机栈

运行时栈帧结构
栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构。

  • 每一个方法从调用开始到执行完成的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。
  • 每一个栈帧都包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。
  • 一个栈帧需要分配多少内存,不会受到程序运行期间变量数据的影响,而仅仅取决于具体的虚拟机实现。

局部变更表(Local Variable Table)

用于存储方法参数和局部变量
变量槽 (Variable Slot)是局部变量表的最小单位(32字节),一个Slot存放boolean byte char short int float reference和returnAddress8种类型。
reference描述对象引用通过它可以找到对象在java堆中的地址索引和该数据类型在方法区的类型信息。
对64字节的long和double,分配2个连接的slot
slot是可以重用的,当slot中的变量离开作用域时,就可以交给其他变量使用。

操作数栈(Operand Stack)

在class文件的code属性的max_stacks指定了栈的最大深度
java解释执行引擎是’基于栈的执行引擎’,这个栈就是操作数栈
方法执行中,调用算术运算或其他的方法进行参数传递的时候,是通过操作数栈进行的。
在jvm的概念模型中,两个栈帧是相互独立的。但绝大多数虚拟机的实现都会进行优化,令两个栈帧的一部分重叠,以便共用一部分数据

动态连接(Dynamic Linking)

字节码中的方法调用 指令就以常量池中指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或者第一次使用的时候 就转化 为直接引用,这种转化称为静态解析。
另一部分在每次运行期间转化为直接引用,这部分称为动态连接。

方法返回地址

当方法执行后,只有两种方式可以退出这个方法。
第一种方式是执行引擎遇到任意该方法返回的字节码指令,这种退出方法称为正常完成出口。

附加信息

虚拟机规范允许虚拟机实现 增加规范里没有描述的信息到栈帧中去。
在实际开发中, 一般把动态连接、方法返回地址和附加信息全部归为一类,称为栈帧信息。


通常人们把java内存分为堆(heap)和栈(stack),这个栈往往就是指虚拟机栈。

  • 每一个线程包含一个stack区,只保存基本数据类型的对象和自定义对象的引用(不是对象),对象都存放在共享heap中;
  • 每个栈中的数据(基本数据类型和对象引用)都是私有的,其他栈不能访问;
  • 每一个方法从调用到执行完成的过程,就对应一个栈帧在虚拟机栈中入栈到出栈的过程。
  • 栈分为3部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)
  • 局部变量表存放基本数据类型、对象引用(referecne)和ruturnAddress(指向 一条字节码指令的地址)。
  • 如果线程请求的栈深度大于虚拟机允许的深度,则抛出StackOverflowError;如果虚拟机可动态扩展,但无法申请到足够的系统内存,则抛出OOM
  • 栈的优势劣势:存取速度比堆要快,仅次于直接位于CPU的寄存器,但必须确定的是存在stack中的数据大小与生存期必须是确定的,缺乏灵活性。单个stack的数据可以共享。
  • stack:是一个先进后出的数据结构,通常保存方法中的参数,局部变量。在java中,所有基本类型和引用类型都在stack中储存,栈中数据的生存空间一般在当前scopes内

本地方法栈

虚拟机栈为虚拟机执行java方法服务,而本地方法栈为虚拟机使用到的Native方法服务。

image.png

Topic 1. 本地方法栈的功能?

  1. 为线程私有,功能和虚拟机栈非常类似。线程在调用本地方法时,来存储本地方法的局部变量表,本地方法的操作数栈等等信息。

Topic 2. 什么是本地方法?为什么要使用本地方法?

  1. 简单地讲,一个本地方法是这样一个方法:该方法的实现由非java语言实现,比如C语言实现。很多其它的编程语言都有这一机制,比如在C++中,你可以告知C++编译器去调用一个C语言编写的方法。<br /> 我们知道java是高级编程语言,当对一些底层的如操作系统或某些硬件交换信息时,我们使用java来编程实现起来不容易,再者使用java来编程效率也很低下。这就不得不需要调用本地方法来解决这一问题。

Topic 3. 本地方法是如何工作的?

  1. 就是当一个线程调用一个本地方法时,本地方法又回调虚拟机中的另一个Java方法。这幅图展示了java虚拟机内部线程运行的全景图。一个线程可能在整个生命周期中都执行Java方法,操作他的Java栈;或者他可能毫无障碍地在Java栈和本地方法栈之间跳转。<br /> 该线程首先调用了两个Java方法,而第二个Java方法又调用了一个本地方法,这样导致虚拟机使用了一个本地方法栈。图中的本地方法栈显示为 一个连续的内存空间。假设这是一个C语言栈,期间有两个C函数,他们都以包围在虚线中的灰色块表示。第一个C函数被第二个Java方法当做本地方法调用, 而这个C函数又调用了第二个C函数。之后第二个C函数被第二个Java方法当做本地方法调用,而这个C函数又调用了第二个C函数。之后第二个C函数又通过 本地方法接口回调了一个Java方法(第三个Java方法)。最终这个Java方法又调用了一个Java方法(他成为图中的当前方法)

堆Heap

  • 堆中 存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令);
  • jvm只有一个heap区,被所有线程共享,不存放基本类型和对象引用,只存放对象本身。
  • 堆的优劣势:堆的优势是可以动态的分配内存大小,生存期也不必事先告诉编译器,java的垃圾收集器会自动收取这些不在使用的数据,但缺点是,由于要在运行时动态分配内存,存取速度慢。
  • Java堆也是垃圾收集管理的主要区域,因此常被称为“GC堆”

方法区(MethodArea)

又称NON-Heap

  • 方法又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量;
  • 方法区中包含的都是在程序中永远的唯一的元素

常量池

直接内存

Q&A

永久代和MetaSpace的区别?
不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存