• 深入理解JVM虚拟机第二十四篇:详解JVM当中的动态链接和常量池的作用


    大神链接:作者有幸结识技术大神孙哥为好友,获益匪浅。现在把孙哥视频分享给大家。

    孙哥链接:孙哥个人主页
    作者简介:一个颜值99分,只比孙哥差一点的程序员
    本专栏简介:话不多说,让我们一起干翻JVM

    本文章简介:话不多说,让我们讲清楚JVM当中与操作数栈相关的动态链接和常量池的作用

    文章目录

    知识回顾

    1:栈帧中的结构图解

    2:结构概念回顾 

    一:动态链接

    1:动态链接概念

    2:编写代码证明

    3:源代码的Javap

    二:常量池

    1:常量池的概念

    2:说明


    知识回顾

    1:栈帧中的结构图解

    2:结构概念回顾 

             栈帧中的几部分大致可以分为这几个:局部变量表,操作数栈,动态链接,方法返回地址,一些附加信息。

            局部变量表,操作数栈我们都已经详细的分析过了,接下来,我们该分享动态链接了。

            值得一提的是:方法返回地址,动态链接和一些附加信息在有一些地方被称为帧数据区。这个概念主要在一些教材上是这样提到的,我们也需要知道。

    一:动态链接

    1:动态链接概念

            动态链接又称为:执行运行时常量池的方法引用。

            每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接(Dynamic Linking)。比如: invokedynamic指令

            在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用 (Symbolic Reference)保存在class文件的常量池里。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用

            常量池就是我们的类被加载的时候所需要的信息,类、属性、方法的各种信息都会使用符号的形式声明出来,这是对类中各类数据的一个描述,而这个描述基于符号引用进行存储到了常量池这个区域。

            而在栈帧中的字节码区域,字节码指令后边跟着一堆符号,这些符号就是常量池中的符号索引。这样就是所谓的:执行运行时常量池的方法引用。

            常量池就是字节码整理后的那个区域,常量池当方法运行时会进入到方法区当中,此时的常量池就被称为运行时常量池。

            动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用 

            这一有一个问题,为什么要通过动态链接这种方式,而不是直接通过将数据直接放到引用的位置形成直接引用呢? 

            我们这样做的目的是为了保证相同数据只有一份,不同地方使用只需要添加引用即可。节约空间。提升运行效率。

            为什么需要这个运行时常量池呢?运行时常量池就是把字节码中的常量池加载到了方法区中,没有这个运行时常量池也行,采用直接引用即可,但是这样就回到了上边的问题。

            所以为什么需要常量池呢?
            动态链接和常量池的作用,就是为了提供一些符号和常量,便于指令的识别。

    2:编写代码证明

    1. /**
    2. * @author Administrator
    3. */
    4. public class DynamicLInkingTest {
    5. int num = 10;
    6. public void methodA(){
    7. System.out.println("methodA().....");
    8. }
    9. public void methodB(){
    10. System.out.println("methodB().....");
    11. methodA();
    12. num++;
    13. }
    14. }

    3:源代码的Javap

            基于Javap对字节码进行整理,按照格式进行输出。

    1. PS D:\code\study\hadoop\shit\target\classes> javap -v .\DynamicLInkingTest.class
    2. Classfile /D:/code/study/hadoop/shit/target/classes/DynamicLInkingTest.class
    3. Last modified 20231112日; size 678 bytes
    4. SHA-256 checksum acf0e245362759ceb374039c92493caf8913ff898697f7735b6ddedb262d2bd7
    5. Compiled from "DynamicLInkingTest.java"
    6. public class DynamicLInkingTest
    7. minor version: 0
    8. major version: 52
    9. flags: (0x0021) ACC_PUBLIC, ACC_SUPER
    10. this_class: #8 // DynamicLInkingTest
    11. super_class: #9 // java/lang/Object
    12. interfaces: 0, fields: 1, methods: 3, attributes: 1
    13. Constant pool:
    14. #1 = Methodref #9.#23 // java/lang/Object."":()V
    15. #2 = Fieldref #8.#24 // DynamicLInkingTest.num:I
    16. #3 = Fieldref #25.#26 // java/lang/System.out:Ljava/io/PrintStream;
    17. #4 = String #27 // methodA().....
    18. #5 = Methodref #28.#29 // java/io/PrintStream.println:(Ljava/lang/String;)V
    19. #6 = String #30 // methodB().....
    20. #7 = Methodref #8.#31 // DynamicLInkingTest.methodA:()V
    21. #8 = Class #32 // DynamicLInkingTest
    22. #9 = Class #33 // java/lang/Object
    23. #10 = Utf8 num
    24. #11 = Utf8 I
    25. #12 = Utf8
    26. #13 = Utf8 ()V
    27. #14 = Utf8 Code
    28. #15 = Utf8 LineNumberTable
    29. #16 = Utf8 LocalVariableTable
    30. #17 = Utf8 this
    31. #18 = Utf8 LDynamicLInkingTest;
    32. #19 = Utf8 methodA
    33. #20 = Utf8 methodB
    34. #21 = Utf8 SourceFile
    35. #22 = Utf8 DynamicLInkingTest.java
    36. #23 = NameAndType #12:#13 // "":()V
    37. #24 = NameAndType #10:#11 // num:I
    38. #25 = Class #34 // java/lang/System
    39. #26 = NameAndType #35:#36 // out:Ljava/io/PrintStream;
    40. #27 = Utf8 methodA().....
    41. #28 = Class #37 // java/io/PrintStream
    42. #29 = NameAndType #38:#39 // println:(Ljava/lang/String;)V
    43. #30 = Utf8 methodB().....
    44. #31 = NameAndType #19:#13 // methodA:()V
    45. #32 = Utf8 DynamicLInkingTest
    46. #33 = Utf8 java/lang/Object
    47. #34 = Utf8 java/lang/System
    48. #35 = Utf8 out
    49. #36 = Utf8 Ljava/io/PrintStream;
    50. #37 = Utf8 java/io/PrintStream
    51. #38 = Utf8 println
    52. #39 = Utf8 (Ljava/lang/String;)V
    53. {
    54. int num;
    55. descriptor: I
    56. flags: (0x0000)
    57. public DynamicLInkingTest();
    58. descriptor: ()V
    59. flags: (0x0001) ACC_PUBLIC
    60. Code:
    61. stack=2, locals=1, args_size=1
    62. 0: aload_0
    63. 1: invokespecial #1 // Method java/lang/Object."":()V
    64. 4: aload_0
    65. 5: bipush 10
    66. 7: putfield #2 // Field num:I
    67. 10: return
    68. LineNumberTable:
    69. line 4: 0
    70. line 6: 4
    71. LocalVariableTable:
    72. Start Length Slot Name Signature
    73. 0 11 0 this LDynamicLInkingTest;
    74. public void methodA();
    75. descriptor: ()V
    76. flags: (0x0001) ACC_PUBLIC
    77. Code:
    78. stack=2, locals=1, args_size=1
    79. 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
    80. 3: ldc #4 // String methodA().....
    81. 5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    82. 8: return
    83. LineNumberTable:
    84. line 9: 0
    85. line 10: 8
    86. LocalVariableTable:
    87. Start Length Slot Name Signature
    88. 0 9 0 this LDynamicLInkingTest;
    89. public void methodB();
    90. descriptor: ()V
    91. flags: (0x0001) ACC_PUBLIC
    92. Code:
    93. stack=3, locals=1, args_size=1
    94. 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
    95. 3: ldc #6 // String methodB().....
    96. 5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    97. 8: aload_0
    98. 9: invokevirtual #7 // Method methodA:()V
    99. 12: aload_0
    100. 13: dup
    101. 14: getfield #2 // Field num:I
    102. 17: iconst_1
    103. 18: iadd
    104. 19: putfield #2 // Field num:I
    105. 22: return
    106. LineNumberTable:
    107. line 13: 0
    108. line 15: 8
    109. line 17: 12
    110. line 18: 22
    111. LocalVariableTable:
    112. Start Length Slot Name Signature
    113. 0 23 0 this LDynamicLInkingTest;
    114. }
    115. SourceFile: "DynamicLInkingTest.java"

            我们关注一下methodB方法的内容:

            我们截取其中的一行的字节码指令,3是地址,或者应该叫偏移地址。ldc是具体的偏移地址下的字节码指令。

             3: ldc           #6                  // String methodB().....

    二:常量池

    1:常量池的概念

            常量池就是我们的类被加载的时候所需要的信息,类、属性、方法的各种信息都会使用符号的形式声明出来,#1=Methodref后边可能继续是符号引用或者是真实的数据。总之跟着符号索引一定可以找到最终的真实数据,这是对类中各类数据的一个描述,而这个描述基于符号引用进行处处到了常量池这个区域。

            而在栈帧中的字节码区域,字节码指令后边跟着一堆符号,这些符号就是常量池中的符号索引。这样就是所谓的:执行运行时常量池的方法引用。

    1. Constant pool:
    2. #1 = Methodref #9.#23 // java/lang/Object."":()V
    3. #2 = Fieldref #8.#24 // DynamicLInkingTest.num:I
    4. #3 = Fieldref #25.#26 // java/lang/System.out:Ljava/io/PrintStream;
    5. #4 = String #27 // methodA().....
    6. #5 = Methodref #28.#29 // java/io/PrintStream.println:(Ljava/lang/String;)V
    7. #6 = String #30 // methodB().....
    8. #7 = Methodref #8.#31 // DynamicLInkingTest.methodA:()V
    9. #8 = Class #32 // DynamicLInkingTest
    10. #9 = Class #33 // java/lang/Object
    11. #10 = Utf8 num
    12. #11 = Utf8 I
    13. #12 = Utf8
    14. #13 = Utf8 ()V
    15. #14 = Utf8 Code
    16. #15 = Utf8 LineNumberTable
    17. #16 = Utf8 LocalVariableTable
    18. #17 = Utf8 this
    19. #18 = Utf8 LDynamicLInkingTest;
    20. #19 = Utf8 methodA
    21. #20 = Utf8 methodB
    22. #21 = Utf8 SourceFile
    23. #22 = Utf8 DynamicLInkingTest.java
    24. #23 = NameAndType #12:#13 // "":()V
    25. #24 = NameAndType #10:#11 // num:I
    26. #25 = Class #34 // java/lang/System
    27. #26 = NameAndType #35:#36 // out:Ljava/io/PrintStream;
    28. #27 = Utf8 methodA().....
    29. #28 = Class #37 // java/io/PrintStream
    30. #29 = NameAndType #38:#39 // println:(Ljava/lang/String;)V
    31. #30 = Utf8 methodB().....
    32. #31 = NameAndType #19:#13 // methodA:()V
    33. #32 = Utf8 DynamicLInkingTest
    34. #33 = Utf8 java/lang/Object
    35. #34 = Utf8 java/lang/System
    36. #35 = Utf8 out
    37. #36 = Utf8 Ljava/io/PrintStream;
    38. #37 = Utf8 java/io/PrintStream
    39. #38 = Utf8 println
    40. #39 = Utf8 (Ljava/lang/String;)V

    2:说明

            当前我们只需要常量池对应了哪块区域即可,后边我们会详细的进行剖析。

  • 相关阅读:
    Pikachu靶场——越权访问漏洞(over permission)
    PDF文档编辑Acrobat Pro DC
    虹科案例 | 虹科HiveMQ助力实现百万辆汽车智能互联
    【vue3】01. 跟着官网学习vue3
    Arnoldi Iteration 思考
    【Lua】脚本入门
    【机器学习300问】121、RNN是如何生成文本的?
    一个好用的k8s代理工具——KtConnect
    UE4 自定义组件编译成功,但是无法在添加到 Actor(Add to Actor) 中找到,无法拖动到 Actor 中附加到 Root 的解决方法
    java基础-第1章-走进java世界
  • 原文地址:https://blog.csdn.net/Facial_Mask/article/details/134363430