虚拟机栈(java stack)

百度图片搜索里的动图搜索功能不错,可以搜索一些动图,展示操作数栈的操作过程,比较形象。这点google差点意思

  • 虚拟机栈(jvm stacks)是线程独占的

  • 里面是多个栈帧(frame)或叫方法帧(class里的每个方法独占一个栈帧,所以也可以称之为方法帧)

  • 每个栈帧里包含:局部变量区/操作数栈/动态链接/方法的返回地址

示例

  1. 文件SimpleExample.java

    1. 1 class SimpleExample {
    2. 2 public static void main(String[] args) {
    3. 3 int result = add(2,3);
    4. 4 System.out.println(result);
    5. 5 }
    6. 6 public static int add(int a, int b) {
    7. 7 return a+b;
    8. 8 }
    9. 9 }
  2. 编译源代码生产字节码文件SimpleExample.class

    -g Generates all debugging information, including local variables. By default, only line number and source file information is generated.

    作用是反编译的时候产生局部变量表

    1. javac -g SimpleExample.java
  3. 反编译

    1. javap -v -l -p SimpleExample.class

    man -a javap查看帮助信息

    -l Prints line and local variable tables.

    -v Prints stack size, number of locals and arguments for methods.

    -p Shows all classes and members.

    1. myJavaDir javap -v -l -p SimpleExample
    2. Classfile /Users/xxxx/myJavaDir/SimpleExample.class
    3. Last modified Oct 29, 2019; size 642 bytes
    4. // 字节码的md5值
    5. MD5 checksum e54540b1d2c0620b7e4eb555a8192a78
    6. Compiled from "SimpleExample.java"
    7. class SimpleExample
    8. minor version: 0
    9. major version: 52
    10. flags: ACC_SUPER
    11. //class常量池
    12. Constant pool:
    13. #1 = Methodref #6.#26 // java/lang/Object."<init>":()V
    14. #2 = Methodref #5.#27 // SimpleExample.add:(II)I
    15. #3 = Fieldref #28.#29 // java/lang/System.out:Ljava/io/PrintStream;
    16. #4 = Methodref #30.#31 // java/io/PrintStream.println:(I)V
    17. #5 = Class #32 // SimpleExample
    18. #6 = Class #33 // java/lang/Object
    19. #7 = Utf8 <init>
    20. #8 = Utf8 ()V
    21. #9 = Utf8 Code
    22. #10 = Utf8 LineNumberTable
    23. #11 = Utf8 LocalVariableTable
    24. #12 = Utf8 this
    25. #13 = Utf8 LSimpleExample;
    26. #14 = Utf8 main
    27. #15 = Utf8 ([Ljava/lang/String;)V
    28. #16 = Utf8 args
    29. #17 = Utf8 [Ljava/lang/String;
    30. #18 = Utf8 result
    31. #19 = Utf8 I
    32. #20 = Utf8 add
    33. #21 = Utf8 (II)I
    34. #22 = Utf8 a
    35. #23 = Utf8 b
    36. #24 = Utf8 SourceFile
    37. #25 = Utf8 SimpleExample.java
    38. #26 = NameAndType #7:#8 // "<init>":()V
    39. #27 = NameAndType #20:#21 // add:(II)I
    40. #28 = Class #34 // java/lang/System
    41. #29 = NameAndType #35:#36 // out:Ljava/io/PrintStream;
    42. #30 = Class #37 // java/io/PrintStream
    43. #31 = NameAndType #38:#39 // println:(I)V
    44. #32 = Utf8 SimpleExample
    45. #33 = Utf8 java/lang/Object
    46. #34 = Utf8 java/lang/System
    47. #35 = Utf8 out
    48. #36 = Utf8 Ljava/io/PrintStream;
    49. #37 = Utf8 java/io/PrintStream
    50. #38 = Utf8 println
    51. #39 = Utf8 (I)V
    52. {
    53. // 默认构造函数,即使我们不写,也会自动产生
    54. SimpleExample();
    55. descriptor: ()V
    56. flags:
    57. Code:
    58. // 栈帧的操作数栈里有一个元素,栈帧的局部变量表里有一个变量(为this即SimpleExample对象),构造方法有一个入参(this),构建方法的第一个参数(和默认参数)是this
    59. stack=1, locals=1, args_size=1
    60. // 把局部变量表(数组结构)里的索引为0的数据(this对象)放入操作数栈
    61. 0: aload_0
    62. // 调用(this)父类(这里是Object)的构造方法
    63. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
    64. // 返回,清空操作数栈和局部变量表
    65. 4: return
    66. // 源代码和字节码指令序号的映射表
    67. LineNumberTable:
    68. // 代码的第一行("class SimpleExample {")对应的指令为0: aload_0
    69. line 1: 0
    70. // 局部变量表
    71. LocalVariableTable:
    72. // 变量名字是this,长度为5个字节。局部变量表第一项是个reference(引用),它指定的就是对象本身的引用,也就是我们常用的this,但是在静态方法中,没这个引用
    73. Start Length Slot Name Signature
    74. 0 5 0 this LSimpleExample;
    75. public static void main(java.lang.String[]);
    76. descriptor: ([Ljava/lang/String;)V
    77. // 方法修饰符public static
    78. flags: ACC_PUBLIC, ACC_STATIC
    79. Code:
    80. // 栈帧里的操作数栈为2,局部变量数量为2,参数个数为1
    81. stack=2, locals=2, args_size=1
    82. // 把常量池里的整数2压入操作数栈
    83. 0: iconst_2
    84. // 把常量池里的整数3压入操作数栈
    85. 1: iconst_3
    86. // 调用class常量池里的#2即静态方法add,返回结果入栈顶。由于该方法需要两个整数做参数,所以invokestatic从操作数栈中弹出两个元素,并将它们传给由JVM为add()创建的新栈帧,保存在add方法帧的局部变量表里。main()的操作数栈此时是空的。
    87. 2: invokestatic #2 // Method add:(II)I
    88. // 把操作数栈的栈顶的一个整数元素取出,存入局部变量表的索引为1的位置,也就是result的值
    89. 5: istore_1
    90. // getstatic将类型为java/io/PrintStream的静态字段java/lang/System.out压入栈中
    91. 6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
    92. // iload_1将局部变量表(数组结构)里的索引位置为1处的变量(就是result的值,现在等于5)压入到操作数栈中。所以此时栈保存了两个值:out字段和值5
    93. 9: iload_1
    94. // 现在invokevirtual准备调用PrintSteam.println()方法。它从栈中弹出两个元素:第一个元素是对将要调用println()方法的对象的引用。第二个元素是一个要传递给println()方法的整型参数,它只需要一个参数。这是main()方法打印相加的结果的地方
    95. 10: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
    96. // return命令完成该方法。主帧被丢弃,JVM进程结束
    97. 13: return
    98. LineNumberTable:
    99. // 源代码第3行(int result = add(2,3);)对应字节码指令(0: iconst_2,1: iconst_3, 2: invokestatic #2,5: istore_1)直到(line 4: 6)
    100. line 3: 0
    101. line 4: 6
    102. line 5: 13
    103. LocalVariableTable:
    104. // main方法栈帧的局部变量变里有两个变量args、result。静态方法,没有this引用,第一个变量就不再是this
    105. Start Length Slot Name Signature
    106. 0 14 0 args [Ljava/lang/String;
    107. 6 8 1 result I
    108. public static int add(int, int);
    109. // (II)I表示方法add为两个int的入参数,返回类型为int
    110. descriptor: (II)I
    111. flags: ACC_PUBLIC, ACC_STATIC
    112. Code:
    113. // 栈帧里的操作数栈为2,局部变量数量为2,参数个数为2
    114. stack=2, locals=2, args_size=2
    115. // 把add方法帧局部变量表里索引为0的元素值(这里为整数2)压入add方法帧的操作数栈
    116. 0: iload_0
    117. // 把add方法帧局部变量表里索引为1的元素值(这里为整数3)压入add方法帧的操作数栈
    118. 1: iload_1
    119. // 把操作数栈的栈顶的2个整数从操作数栈里取出,然后相加,最后把结果再次存入操作数栈
    120. 2: iadd
    121. // 正常情况下,当前帧操作数栈的栈顶值出栈,压入调用方法的操作上栈的栈顶,丢弃当前帧操作数栈的其他值,销毁了add方法帧。恢复到调用方法帧,调用方法(invoker)继续执行
    122. 3: ireturn
    123. LineNumberTable:
    124. // 源代码的第7行(return a+b;)对应上面的字节码指令(0: iload_0)及后续指令
    125. line 7: 0
    126. LocalVariableTable:
    127. // 栈帧里的局部变量表里有2个变量a和b,类型为int,大小都为4个字节,在局部变量表里的索引位置为0和1
    128. Start Length Slot Name Signature
    129. 0 4 0 a I
    130. 0 4 1 b I
    131. }
    132. SourceFile: "SimpleExample.java"

示例:

  1. package com.demo3;
  2. public class Test {
  3. public static void foo() {
  4. int a = 1;
  5. int b = 2;
  6. int c = (a + b) * 5;
  7. }
  8. public static void main(String[] args) {
  9. foo();
  10. }
  11. }

基于栈的Hotspot的执行过程如下:

参考

浅谈虚拟机内存模型

Java虚拟机—栈帧、操作数栈和局部变量表

JVM体系结构101:了解虚拟机

jvm指令集

通过javap命令分析java汇编指令

Java虚拟机栈(java stack)的更多相关文章

  1. Java虚拟机栈和PC寄存器

    PC Register介绍 JVM中的程序计数寄存器(Program Counter Register)中,Register 的命名源于CPU的寄存器,寄存器存储指令相关的现场信息.CPU只有把数据装 ...

  2. Java虚拟机栈 和 方法区 的联系

    1.Java虚拟机栈 java方法执行时的内存模型 1.1 栈帧 每个方法都会在虚拟机栈中创建一个对应的栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息. 一个方法的调用到结束就对应这一个 ...

  3. 深入理解Java虚拟机之Java内存区域与内存溢出异常

    Java内存区域与内存溢出异常 运行时数据区域 程序计数器 用于记录从内存执行的下一条指令的地址,线程私有的一小块内存,也是唯一不会报出OOM异常的区域 Java虚拟机栈 Java虚拟机栈(Java ...

  4. JVM 运行时数据区:程序计数器、Java 虚拟机栈和本地方法栈,方法区、堆以及直接内存

    Java 虚拟机可以看作一台抽象的计算机,如同真实的计算机,它也有自己的指令集和运行时内存区域. Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存(运行时内存区域)划分为若干个不同的数 ...

  5. Java虚拟机栈---本地方法栈

    1.Java虚拟机栈(Java Virtual Machine Stacks) 线程私有,它的生命周期与线程相同.描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack ...

  6. JVM运行时数据区--Java虚拟机栈

    虚拟机栈的背景 由于跨平台性的设计,java的指令都是根据栈来设计的.不同平台CPU架构不同,所以不能设计为基于寄存器的. 根据栈设计的优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样 ...

  7. Java JVM——5.Java虚拟机栈

    虚拟机栈概述 由于跨平台性的设计,Java 的指令都是根据栈来设计的.不同平台CPU架构不同,所以不能设计为基于寄存器的. 栈实现的优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功 ...

  8. Java虚拟机栈和本地方法栈

    Java虚拟机栈的特征 线程私有 后进先出(LIFO)栈 存储栈帧,支持Java方法的调用.执行和退出 可能出现OutOfMemoryError异常和StackOverflowError异常 Java ...

  9. java 虚拟机栈

    与程序计数器一样,Java虚拟机栈也是线程私有的,他的生命周期与线程相同.虚拟机栈描述的是Java执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口 ...

随机推荐

  1. Maven之setting.xml 配置详解

    文件存放位置 全局配置: ${M2_HOME}/conf/settings.xml 用户配置: ${user.home}/.m2/settings.xml note:用户配置优先于全局配置.${use ...

  2. 小程序~WeUI下载使用

    [1]简介 因为小程序的api描述都比较简单,并没有wxml及wxss的描述,一定会想小程序有没有一个UI库,类似于前端中的Bootstrap,MD,Semantic UI这样的框架UI库.有的,它就 ...

  3. dt系统中tag如何使用like与%来进行模糊查询

    在destoon中,如果一个品牌的详细显示页,如果要显示与品牌相关的供应的话,可以通过查询标题中带有品牌关键字的这一条件来进行查询,但是经过测试发现不能正确解析, 然后查看文件的源文件,发现 {tag ...

  4. 20199301《Linux内核原理与分析》第十一周作业

    Linux Capability探索实验 一.实验描述 本实验中,将感受到linux capability功能在访问控制上的优势,掌握使用Capability达到遵守最小权限原则的目的,并分析linu ...

  5. Jenkins拉取github库代码执行构建

    前言 上篇文章写了关于定时构建,以及构建后发送邮件的内容,但是构建时运行的代码是我们手动添加到Jenkins工作空间的.这篇文章我们说一说自动从GitHub远程库拉取代码,执行构建,废话不多说,开始! ...

  6. 使用SpringBoot访问jsp页面

    1 编写application.yml文件 spring: mvc: view: suffix: .jsp prefix: /jsp/ 2 创建Controller层 @Controller @Req ...

  7. robot framework中如何为每个测试用例,测试集准备数据或销毁数据

    Suite Setup:在这个测试集的所有测试用例开始测试之前运行(类似于junit的@BeforeClass) Suite Teardown:在这个测试集的所有测试用例结束之后运行(类似于junit ...

  8. area标签的使用,图片中某一个部分可以点击跳转,太阳系中点击某个行星查看具体信息

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  9. mybatis自动生成model、dao及对应的mapper.xml文件

    背景: 日常开发中,如果新建表,手动敲写model.dao和对应的mapper.xml文件,费时费力且容易出错, 所以采用mybatis自动生成model.dao及对应的mapper.xml文件.代码 ...

  10. learning java Paths Path

    import java.nio.file.Path; import java.nio.file.Paths; public class PathTest { public static void ma ...