专业的JAVA编程教程与资源

网站首页 > java教程 正文

「JVM系列」 从一到掌握JVM系列之Java虚拟机栈

temp10 2024-11-12 13:02:21 java教程 12 ℃ 0 评论

前言

本文主要介绍如何结合字节码指令理解Java虚拟机栈和栈帧,并进行深入分析以及对内存模型的介绍和验证。

虚拟机栈概述

由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台 CPU 架构不同,所以不能设计为基于寄存器的。 优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令。 有不少 Java 开发人员一提到Java内存结构,就会非常粗粒度地将JVM中的内存区理解为仅有 Java 堆(heap)和 Java 栈(stack)?为什么? 首先栈是运行时的单位,而堆是存储的单位

「JVM系列」 从一到掌握JVM系列之Java虚拟机栈

  • 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。
  • 堆解决的是数据存储的问题,即数据怎么放,放哪里。

Java虚拟机栈是什么

  • Java虚拟机栈(Java Virtual Machine Stack),早期也叫 Java 栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的 Java 方法调用。
  • 是线程私有的
  • 生命周期

生命周期和线程一致,也就是线程结束了,该虚拟机栈也销毁了

  • 作用

主管Java程序的运行,它保存方法的局部变量、部分结果,并参与方法的调用和返回。

  • 局部变量,它是相比于成员变量来说的(或属性)
  • 基本数据类型变量 VS 引用类型变量(类、数组、接口)

栈的特点

  • 栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器。
  • JVM 直接对 Java 栈的操作只有两个:每个方法执行,伴随着进栈(入栈、压栈)执行结束后的出栈工作
  • 对于栈来说不存在垃圾回收问题(栈存在溢出的情况)GC;OOM

结合字节码指令理解Java虚拟机栈和栈帧

栈帧

每个栈帧对应一个被调用的方法,可以理解为一个方法的运行空间。

每个栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向运行时常量池的引用(A reference to the run-time constant pool)、方法返回地址(Return Address)和附加信息。 【领取资料】

局部变量表

方法中定义的局部变量以及方法的参数存放在这张表中,局部变量表中的变量不可直接使用,如需要使用的话,必须通过相关指令将其加载至操作数栈中作为操作数使用。

操作数栈

以压栈和出栈的方式存储操作数的。

动态链接

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。

方法返回地址:当一个方法开始执行后,只有两种方式可以退出,一种是遇到方法返回的字节码指令;一种是遇见异常,并且这个异常没有在方法体内得到处理。

代码展示

class Person{
 private String name="Jack";
 private int age;
 private final double salary=100;
 private static String address;
 private final static String hobby="Programming";

 public void say(){
 System.out.println("person say..."); 
 }

 public static int calc(int op1,int op2){
 op1=3;
 int result=op1+op2;
 return result;
 }

 public static void order(){
 }

 public static void main(String[] args){
 calc(1,2);
 order(); 
 }
 }
Compiled from "Person.java" class Person {
 ...
 public static int calc(int, int);
 Code:
 0: iconst_3 //将int类型常量3压入[操作数栈]
 1: istore_0 //将int类型值存入[局部变量0]
 2: iload_0 //从[局部变量0]中装载int类型值入栈
 3: iload_1 //从[局部变量1]中装载int类型值入栈
 4: iadd //将栈顶元素弹出栈,执行int类型的加法,结果入栈
 【For example, the iadd instruction (§iadd) adds two int 
 values together. It requires that the int values to
 be added be the top two values of the operand stack, 
 pushed there by previous instructions. Both of the int values
 are popped from the operand stack. 
 They are added, and their sum is pushed back onto the operand stack. 
 Subcomputations may be nested on the operand stack, 
 resulting in values that can be used by the encompassing computation.】
 5: istore_2 //将栈顶int类型值保存到[局部变量2]中
 6: iload_2 //从[局部变量2]中装载int类型值入栈
 7: ireturn //从方法中返回int类型的数据
 ...
 }

深入分析

栈指向堆

如果在栈帧中有一个变量,类型为引用类型,比如 Object obj=new Object(),这时候就是典型的栈中元素指向堆中的对象。

方法区指向堆

方法区中会存放静态变量,常量等数据。如果是下面这种情况,就是典型的方法区中元素指向堆中的对象。

private static Object obj=new Object();

堆指向方法区

方法区中会包含类的信息,堆中会有对象,那怎么知道对象是哪个类创建的呢?

总结

最后,感谢大家的观看,谢谢大家的支持,最好是一键三连!

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表