• 深入理解JVM(十九)使用javap指令解析Class文件


    一、解析字节码的作用

    通过反编译生成的字节码文件,我们可以深入的了解java代码的工作机制。但是,自己分析类文件太麻烦了,除了使用第三方的jclasslib工具之外,oracle官方也提供了工具: javap。

    javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区(字节码指令)、局部变量表、异常表和代码行偏移量映射表、常量池等信息。

    通过局部变量表,我们可以查看局部变量的作用域范围、所在槽位等信息,甚至可以看到槽位复用等信息。

    二、javac -g 说明

     -g  在生成的class文件中包含所有调试信息(行号、变量、源文件)

    这个参数在javac编译中是看不到什么作用的,因为调试信息都在class文件中。 

    在使用javac编译成class文件时,有些信息(如局部变量表、指令和代码行偏移量映射表、常量池中方法等)需要指定某些参数才能输出。

    比如,你直接javac xx.java, 就不会生成对应的局部变量表等信息,如果你使用javac -g xx. java就可以生成所有相关信息了

    默认情况下,eclipse、 IDEA在编译时会帮你生成局部变量表、指令和代码行偏移量映射表等信息的。

    三、javap的用法

    在这里插入图片描述

    四、使用例子  

    1. public class JavapTest {
    2. private int num;
    3. boolean flag;
    4. protected char gender;
    5. public String info;
    6. public static final int COUNTS = 1;
    7. static {
    8. String url = "www.atguigu.com";
    9. }
    10. {
    11. info = "java";
    12. }
    13. public JavapTest() {
    14. }
    15. private JavapTest(boolean falg) {
    16. this.flag = flag;
    17. }
    18. private void methodPrivate() {
    19. }
    20. int getNum(int i) {
    21. return num + i;
    22. }
    23. protected char showGender() {
    24. return gender;
    25. }
    26. public void showInfo() {
    27. int i = 100;
    28. System.out.println(info + i);
    29. }
    30. }
     javac -g JavapTest.java
    javap -v -p JavapTest.class
    1. Classfile /D:/test/JavapTest.class // 字节码文件所属的路径
    2. Last modified 2022-9-9; size 1332 bytes // 最后修改时间,字节码文件的大小
    3. MD5 checksum b51bfb9f03b6513a89bc45cd775d7d35 // MD5散列值
    4. Compiled from "JavapTest.java" // 源文件的名称
    5. public class JavapTest
    6. minor version: 0 // 副版本
    7. major version: 52 // 主版本
    8. flags: ACC_PUBLIC, ACC_SUPER // 访问标识
    9. /*************************** 常量池********************************/
    10. Constant pool:
    11. #1 = Methodref #16.#47 // java/lang/Object."":()V
    12. #2 = String #48 // java
    13. #3 = Fieldref #15.#49 // JavapTest.info:Ljava/lang/String;
    14. #4 = Fieldref #15.#50 // JavapTest.flag:Z
    15. #5 = Fieldref #15.#51 // JavapTest.num:I
    16. #6 = Fieldref #15.#52 // JavapTest.gender:C
    17. #7 = Fieldref #53.#54 // java/lang/System.out:Ljava/io/PrintStream;
    18. #8 = Class #55 // java/lang/StringBuilder
    19. #9 = Methodref #8.#47 // java/lang/StringBuilder."":()V
    20. #10 = Methodref #8.#56 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    21. #11 = Methodref #8.#57 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
    22. #12 = Methodref #8.#58 // java/lang/StringBuilder.toString:()Ljava/lang/String;
    23. #13 = Methodref #59.#60 // java/io/PrintStream.println:(Ljava/lang/String;)V
    24. #14 = String #61 // www.atguigu.com
    25. #15 = Class #62 // JavapTest
    26. #16 = Class #63 // java/lang/Object
    27. #17 = Utf8 num
    28. #18 = Utf8 I
    29. #19 = Utf8 flag
    30. #20 = Utf8 Z
    31. #21 = Utf8 gender
    32. #22 = Utf8 C
    33. #23 = Utf8 info
    34. #24 = Utf8 Ljava/lang/String;
    35. #25 = Utf8 COUNTS
    36. #26 = Utf8 ConstantValue
    37. #27 = Integer 1
    38. #28 = Utf8 <init>
    39. #29 = Utf8 ()V
    40. #30 = Utf8 Code
    41. #31 = Utf8 LineNumberTable
    42. #32 = Utf8 LocalVariableTable
    43. #33 = Utf8 this
    44. #34 = Utf8 LJavapTest;
    45. #35 = Utf8 (Z)V
    46. #36 = Utf8 falg
    47. #37 = Utf8 methodPrivate
    48. #38 = Utf8 getNum
    49. #39 = Utf8 (I)I
    50. #40 = Utf8 i
    51. #41 = Utf8 showGender
    52. #42 = Utf8 ()C
    53. #43 = Utf8 showInfo
    54. #44 = Utf8 <clinit>
    55. #45 = Utf8 SourceFile
    56. #46 = Utf8 JavapTest.java
    57. #47 = NameAndType #28:#29 // "":()V
    58. #48 = Utf8 java
    59. #49 = NameAndType #23:#24 // info:Ljava/lang/String;
    60. #50 = NameAndType #19:#20 // flag:Z
    61. #51 = NameAndType #17:#18 // num:I
    62. #52 = NameAndType #21:#22 // gender:C
    63. #53 = Class #64 // java/lang/System
    64. #54 = NameAndType #65:#66 // out:Ljava/io/PrintStream;
    65. #55 = Utf8 java/lang/StringBuilder
    66. #56 = NameAndType #67:#68 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    67. #57 = NameAndType #67:#69 // append:(I)Ljava/lang/StringBuilder;
    68. #58 = NameAndType #70:#71 // toString:()Ljava/lang/String;
    69. #59 = Class #72 // java/io/PrintStream
    70. #60 = NameAndType #73:#74 // println:(Ljava/lang/String;)V
    71. #61 = Utf8 www.atguigu.com
    72. #62 = Utf8 JavapTest
    73. #63 = Utf8 java/lang/Object
    74. #64 = Utf8 java/lang/System
    75. #65 = Utf8 out
    76. #66 = Utf8 Ljava/io/PrintStream;
    77. #67 = Utf8 append
    78. #68 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
    79. #69 = Utf8 (I)Ljava/lang/StringBuilder;
    80. #70 = Utf8 toString
    81. #71 = Utf8 ()Ljava/lang/String;
    82. #72 = Utf8 java/io/PrintStream
    83. #73 = Utf8 println
    84. #74 = Utf8 (Ljava/lang/String;)V
    85. /******************************字段表集合的信息**************************************/
    86. {
    87. private int num; // 字段名
    88. descriptor: I // 字段表集合的信息
    89. flags: ACC_PRIVATE // 字段的访问标识
    90. boolean flag;
    91. descriptor: Z
    92. flags:
    93. protected char gender;
    94. descriptor: C
    95. flags: ACC_PROTECTED
    96. public java.lang.String info;
    97. descriptor: Ljava/lang/String;
    98. flags: ACC_PUBLIC
    99. public static final int COUNTS;
    100. descriptor: I
    101. flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    102. ConstantValue: int 1 // 常量字段的属性:ConstantValue
    103. /******************************方法表集合的信息**************************************/
    104. public JavapTest(); // 无参构造器方法信息
    105. descriptor: ()V
    106. flags: ACC_PUBLIC
    107. Code:
    108. stack=2, locals=1, args_size=1
    109. 0: aload_0
    110. 1: invokespecial #1 // Method java/lang/Object."":()V
    111. 4: aload_0
    112. 5: ldc #2 // String java
    113. 7: putfield #3 // Field info:Ljava/lang/String;
    114. 10: return
    115. LineNumberTable:
    116. line 14: 0
    117. line 12: 4
    118. line 16: 10
    119. LocalVariableTable:
    120. Start Length Slot Name Signature
    121. 0 11 0 this LJavapTest;
    122. private JavapTest(boolean); // 单个参数构造器方法信息
    123. descriptor: (Z)V
    124. flags: ACC_PRIVATE
    125. Code:
    126. stack=2, locals=2, args_size=2
    127. 0: aload_0
    128. 1: invokespecial #1 // Method java/lang/Object."":()V
    129. 4: aload_0
    130. 5: ldc #2 // String java
    131. 7: putfield #3 // Field info:Ljava/lang/String;
    132. 10: aload_0
    133. 11: aload_0
    134. 12: getfield #4 // Field flag:Z
    135. 15: putfield #4 // Field flag:Z
    136. 18: return
    137. LineNumberTable:
    138. line 17: 0
    139. line 12: 4
    140. line 18: 10
    141. line 19: 18
    142. LocalVariableTable:
    143. Start Length Slot Name Signature
    144. 0 19 0 this LJavapTest;
    145. 0 19 1 falg Z
    146. private void methodPrivate();
    147. descriptor: ()V
    148. flags: ACC_PRIVATE
    149. Code:
    150. stack=0, locals=1, args_size=1
    151. 0: return
    152. LineNumberTable:
    153. line 22: 0
    154. LocalVariableTable:
    155. Start Length Slot Name Signature
    156. 0 1 0 this LJavapTest;
    157. int getNum(int);
    158. descriptor: (I)I
    159. flags:
    160. Code:
    161. stack=2, locals=2, args_size=2
    162. 0: aload_0
    163. 1: getfield #5 // Field num:I
    164. 4: iload_1
    165. 5: iadd
    166. 6: ireturn
    167. LineNumberTable:
    168. line 24: 0
    169. LocalVariableTable:
    170. Start Length Slot Name Signature
    171. 0 7 0 this LJavapTest;
    172. 0 7 1 i I
    173. protected char showGender();
    174. descriptor: ()C
    175. flags: ACC_PROTECTED
    176. Code:
    177. stack=1, locals=1, args_size=1
    178. 0: aload_0
    179. 1: getfield #6 // Field gender:C
    180. 4: ireturn
    181. LineNumberTable:
    182. line 27: 0
    183. LocalVariableTable:
    184. Start Length Slot Name Signature
    185. 0 5 0 this LJavapTest;
    186. public void showInfo(); // 方法的描述符:方法的形参列表、返回值类型
    187. descriptor: ()V // 方法的访问标识
    188. flags: ACC_PUBLIC // 方法的Code属性
    189. Code: // stack:操作数栈的最大深度 locals:局部变量表的长度 args_size:方法接受参数的个数
    190. stack=3, locals=2, args_size=1
    191. // 偏移量 操作码 操作数
    192. 0: bipush 100
    193. 2: istore_1
    194. 3: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
    195. 6: new #8 // class java/lang/StringBuilder
    196. 9: dup
    197. 10: invokespecial #9 // Method java/lang/StringBuilder."":()V
    198. 13: aload_0
    199. 14: getfield #3 // Field info:Ljava/lang/String;
    200. 17: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    201. 20: iload_1
    202. 21: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
    203. 24: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    204. 27: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    205. 30: return
    206. // 行号表:指明字节码指令的偏移量与java源代码中代码的行号的一一对应关系
    207. LineNumberTable:
    208. line 30: 0
    209. line 31: 3
    210. line 32: 30
    211. // 局部变量表:描述内部局部变量的相关信息
    212. LocalVariableTable:
    213. Start Length Slot Name Signature
    214. 0 31 0 this LJavapTest;
    215. 3 28 1 i I
    216. static {};
    217. descriptor: ()V
    218. flags: ACC_STATIC
    219. Code:
    220. stack=1, locals=1, args_size=0
    221. 0: ldc #14 // String www.atguigu.com
    222. 2: astore_0
    223. 3: return
    224. LineNumberTable:
    225. line 9: 0
    226. line 10: 3
    227. LocalVariableTable:
    228. Start Length Slot Name Signature
    229. }
    230. SourceFile: "JavapTest.java"// 附加属性:指明当前字节码文件对应的源程序文件名

    jclasslib展示的内容:

    五、总结 

    1、通过javap命令可以查看一个java 类反汇编得到的Class文件版本号、常量池、访问标识、 变量表、指令代码行号表等信息。不显示类索引、父类索引、接口索引集合、()、 ()等结构

    2、通过对前面例子代码反汇编文件的简单分析,可以发现,一个方法的执行通常会涉及下面几块内存的操作

    • java栈中:局部变量表、操作数栈。
    • java堆:通过对象的地址引用去操作。
    • 常量池
    • 其他如帧数据区、方法区的剩余部分等情况,测试中没有显示出来,这里说明一下。

    3、平常,我们比较关注的是java类中每个方法的反汇编中的指令操作过程,这些指令都是顺序执行的,可以参考官方文档查看每个指令的含义,很简单:


    Chapter 6. The Java Virtual Machine Instruction Set (oracle.com)

  • 相关阅读:
    ISO体系认证办理流程
    【TypeScript学习】—编译选项(三)
    DSPE-PEG-VIP,磷脂-聚乙二醇-血管活性肠肽VIP,VIP修饰脂质体供应
    【无标题】
    GEE案例——如何进行重采样(分辨率由高分辨率降为低分辨率)以sentinel2为例重采样到1000米
    bmp图片处理
    Redis之性能指标、监控方式
    微前端 - micro-app 数据通信
    Java中的金钱陷阱
    分析Python爬虫设计
  • 原文地址:https://blog.csdn.net/qq_51409098/article/details/126779667