• jvm深入研究文档-探索虚拟机栈底层代码到底是如何实现的?--jvm底层探索(3)


    这个是这个系列的上一个文章(传送门):

    jvm深入研究文档--程序执行专业户-虚拟机栈--jvm底层探索(2)_一单成的博客-CSDN博客

    阿丹:

            在上一个文章中,主要探讨了虚拟机栈的主要组成成员以及中间的数据结构。那么这个文章就从代码的角度出发来看一看经过编译之后的代码,在jvm的影响下面到底是什么执行的,并且是怎么完成我们之前看着十分晦涩难懂的进站压栈出栈的。

    小小的复习一下:

    在jvm的虚拟机栈中的组成为一个一个栈帧,在每个栈帧呢都是线程私有的,每个栈帧都包含了,局部变量表、操作数据表、动态链接和方法返回地址(值得注意的是方法返回地址在官方的说法下面一共有两个,一个是正常的方法出口,另一个是异常的方法出口)。

    本文章的目标:

            我们来探讨和研究一下在这个底层到底是如何试进行数据的调用的以及如何进行数据的操作影响的在一个简单的方法中。

    JVM字节码指令大全

    实例代码:

    1. public class Demo01 {
    2. private final int adn = 20;
    3. public int compute(int num){
    4. return adn + num;
    5. }
    6. public static void main(String[] args) {
    7. Demo01 demo01 = new Demo01();
    8. int res = demo01.compute(50);
    9. System.out.println(res);
    10. }
    11. }

    这段代码定义了一个名为Demo01的公共类。在这个类中,有一个私有的常量adn,其值为20。

    接下来,定义了一个名为compute的公共方法,该方法接受一个整数参数num,并返回adnnum之和。

    main方法中,创建了Demo01类的一个实例demo01,然后调用compute方法并传入50作为参数,将返回的结果赋值给变量res。最后,使用System.out.println打印出res的值。

    这段代码很简单就是一个简简单单的数相加。

    我们通过代码编译将这个代码转换成jvm编译过的代码。

    我们来将这个java代码转换成对应的字节码指令集

    我来描述一下在IDEA中的具体操作,如果动手能力强的小伙伴可以跟着我一起来尝试一下!

    1、在idea中找到我们的代码

    右击 

    2、 使用代码将java代码编译为.class文件

    java .\Demo01.java

     3、使用javap来将生成的字节码输出到一个txt文件中

    javap -c -v -p -l .\Demo01.class > "Demo01.txt"

    对这个代码进行解释一下:

            -c对代码进行反编译

            -v输出附加信息,常量池等

            -l输出局部变量

            -p显示私有内容 

    注意:还是在刚才的路径中和刚才的终端中的!

    这个时候我们在本文件夹下面就能看到我们需要的文件内容了。

    1. Classfile /E:/desktop/stack/Demo01.class
    2. Last modified 2023-9-25; size 547 bytes
    3. MD5 checksum 291623c4dbf535dee20c1688119b9603
    4. Compiled from "Demo01.java"
    5. public class Demo01
    6. minor version: 0
    7. major version: 61
    8. flags: ACC_PUBLIC, ACC_SUPER
    9. Constant pool:
    10. #1 = Methodref #2.#3 // java/lang/Object."":()V
    11. #2 = Class #4 // java/lang/Object
    12. #3 = NameAndType #5:#6 // "":()V
    13. #4 = Utf8 java/lang/Object
    14. #5 = Utf8
    15. #6 = Utf8 ()V
    16. #7 = Fieldref #8.#9 // Demo01.adn:I
    17. #8 = Class #10 // Demo01
    18. #9 = NameAndType #11:#12 // adn:I
    19. #10 = Utf8 Demo01
    20. #11 = Utf8 adn
    21. #12 = Utf8 I
    22. #13 = Methodref #8.#3 // Demo01."":()V
    23. #14 = Methodref #8.#15 // Demo01.compute:(I)I
    24. #15 = NameAndType #16:#17 // compute:(I)I
    25. #16 = Utf8 compute
    26. #17 = Utf8 (I)I
    27. #18 = Fieldref #19.#20 // java/lang/System.out:Ljava/io/PrintStream;
    28. #19 = Class #21 // java/lang/System
    29. #20 = NameAndType #22:#23 // out:Ljava/io/PrintStream;
    30. #21 = Utf8 java/lang/System
    31. #22 = Utf8 out
    32. #23 = Utf8 Ljava/io/PrintStream;
    33. #24 = Methodref #25.#26 // java/io/PrintStream.println:(I)V
    34. #25 = Class #27 // java/io/PrintStream
    35. #26 = NameAndType #28:#29 // println:(I)V
    36. #27 = Utf8 java/io/PrintStream
    37. #28 = Utf8 println
    38. #29 = Utf8 (I)V
    39. #30 = Utf8 ConstantValue
    40. #31 = Integer 20
    41. #32 = Utf8 Code
    42. #33 = Utf8 LineNumberTable
    43. #34 = Utf8 main
    44. #35 = Utf8 ([Ljava/lang/String;)V
    45. #36 = Utf8 SourceFile
    46. #37 = Utf8 Demo01.java
    47. {
    48. private final int adn;
    49. descriptor: I
    50. flags: ACC_PRIVATE, ACC_FINAL
    51. ConstantValue: int 20
    52. public Demo01();
    53. descriptor: ()V
    54. flags: ACC_PUBLIC
    55. Code:
    56. stack=2, locals=1, args_size=1
    57. 0: aload_0
    58. 1: invokespecial #1 // Method java/lang/Object."":()V
    59. 4: aload_0
    60. 5: bipush 20
    61. 7: putfield #7 // Field adn:I
    62. 10: return
    63. LineNumberTable:
    64. line 1: 0
    65. line 3: 4
    66. public int compute(int);
    67. descriptor: (I)I
    68. flags: ACC_PUBLIC
    69. Code:
    70. stack=2, locals=2, args_size=2
    71. 0: bipush 20
    72. 2: iload_1
    73. 3: iadd
    74. 4: ireturn
    75. LineNumberTable:
    76. line 6: 0
    77. public static void main(java.lang.String[]);
    78. descriptor: ([Ljava/lang/String;)V
    79. flags: ACC_PUBLIC, ACC_STATIC
    80. Code:
    81. stack=2, locals=3, args_size=1
    82. 0: new #8 // class Demo01
    83. 3: dup
    84. 4: invokespecial #13 // Method "":()V
    85. 7: astore_1
    86. 8: aload_1
    87. 9: bipush 50
    88. 11: invokevirtual #14 // Method compute:(I)I
    89. 14: istore_2
    90. 15: getstatic #18 // Field java/lang/System.out:Ljava/io/PrintStream;
    91. 18: iload_2
    92. 19: invokevirtual #24 // Method java/io/PrintStream.println:(I)V
    93. 22: return
    94. LineNumberTable:
    95. line 10: 0
    96. line 11: 8
    97. line 12: 15
    98. line 13: 22
    99. }
    100. SourceFile: "Demo01.java"

    现在我们开始正式解析一下这个字节码中的内容

    jvm字节码大全

    我在这里提供了一个连接是字节码的内容,需要的可以在这里拿取,并且下面的研究会用到。

    我们能发现这个字节码虽然不是很认识但是发现中间类的结构大体还是没变化的。

     这里为类的定义

    这里为我们的常量

     

    这里为无参构造

     这个就是我们自己写的计算方法

     这里是我们写的main方法

     解释:

            在每个方法中分了三个部分

    1、code:对应的指令集

    2、LineNumberTable:代码的行数和前面的代码的偏移量之间的关系

    3、LocalVariableTable:这个就是局部变量表了

    我们的重点就是我们的计算方法,我们要研究一下具体是如何使用虚拟机栈来完成操作的

     1、找到对应方法的指令集

    根据下面的code,看到第一条指令为bipush

    我们根据JVM字节码指令查看一下

    https://www.cnblogs.com/ageovb/p/15187314.html#%E5%8F%82%E8%80%83

     能看到它是将我们的常量20推送到栈顶,也就是压栈

     第二条字节码指令为:iload_1

     那么现在就引入了一个概念为局部变量,那么这个局部变量从哪里来的?

    我们知道在本地应用数据可以直接使用this来完成本地应用。

    并且我们的常量在这个文件生成的时候就已经读取进来了,本地变量的表中的数据是根据数据的数据来完成装载的。那么就是说此时的本地变量为:

    常量可以使用this来表示并且引用。 那么这个我上面的图中的p1的名字实际上在我们的代码中应该是num!!!!

    那此时的

    就是将我们现在的赋值了的形参压入栈顶,我们将num复制为50

     第三步字节码指令:iadd

     

    第四步字节码指令: ireturn

    这个就是将数据拿出来放到主main方法中了。

    那么在上述的方法中呢,只是对本地变量表读取。那么我们在mian方法中就有对于数据进行操作的。

    在main方法中的数据操作本地变量表的指令

    第一个指向是方法的调用,就是上面我们在讨论的方法,当获取到返回值之后将数据存到本地的第二个局部变量。因为现在栈中只剩下了一个计算过的数据。

    在本地方法中的第一个位置是常量,那么在这个线程中的第二个数据就是我们刚才计算出来的那个了。

    这个操作数栈和本地变量表就像是我们在草稿纸上操作数进行了计算然后将答案写在了本地方法栈上,并将草稿纸删掉。

    动态链接是啥?:

    可以看到有些指令后面带了井号和数字,这个数字就是指向常量池中的引用。

    引用的具体就是在后面的//注释的东西,就是引用的内容。

    这里就是常量池,我们通过在-p之后添加-v的方法来将常量池进行打印!!! 

  • 相关阅读:
    工作总结:kafka踩过的坑
    基于javaweb+mysql的jsp+servlet学生成绩管理系统(管理员、教师、学生)
    常见概率分布介绍
    a-table 表格拖拽
    多水站送水桶装水配送员公众号H5开发
    你真的懂ArrayList吗?
    1600*G. Special Permutation(构造&找规律)
    TP6 TP8 使用阿里官方OSS SDK方法
    springboot实验室自主预约系统毕业设计源码111953
    践行这两个方法,跳出一切情绪的制约
  • 原文地址:https://blog.csdn.net/weixin_72186894/article/details/133272416