认识class文件

image.png

二进制字节流

硬盘上的文件都是以二进制流的方式存储的

类文件结构

  • Magic Number 魔数 文件类型, .class的16进制魔数就是cafebabe
  • minor version+major version 小大版本号 jdk8对应的版本号是52,其16进制是0034
  • constant_poll_count 常量池中常量数量
  • constant_poll 常量池是一个长度=constant_poll_count-1的列表,保留了空的0位
  • 权限标识
  • 类名 、父类
  • 接口数量 所有接口
  • 成员变量总数 成员变量
  • 方法总数 方法
  • 属性数 总属性

    演示

    1. 8 public class ClassViewer {
    2. 9 private int c;
    3. 10 public String add(String a){
    4. 11 int i =0 ,j=10;
    5. 12 i++;
    6. 13 c += j;
    7. 14 return a+i+j;
    8. 15 }
    9. 16}

    16进制

    image.png

    Javap -v ClassViewer.class

    下述class类信息块 所说的栈 和 栈顶,不做特殊说明 都指的是操作数栈

    1. Last modified 2021-3-30; size 558 bytes
    2. MD5 checksum 9007716478deccca8618ff428c3c57dc
    3. Compiled from "ClassViewer.java"
    4. public class com.?.ClassViewer
    5. minor version: 0
    6. major version: 52 //jdk的MarjorVersion=52
    7. flags: ACC_PUBLIC, ACC_SUPER // 访问权限修饰符
    8. Constant pool:
    9. #1 = Methodref #9.#20 // java/lang/Object."<init>":()V
    10. #2 = Fieldref #8.#21 // com/zhz/jvm/ClassViewer.c:I
    11. #3 = Class #22 // java/lang/StringBuilder
    12. #4 = Methodref #3.#20 // java/lang/StringBuilder."<init>":()V
    13. #5 = Methodref #3.#23 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    14. #6 = Methodref #3.#24 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
    15. #7 = Methodref #3.#25 // java/lang/StringBuilder.toString:()Ljava/lang/String;
    16. #8 = Class #26 // thisClass: com/?/ClassViewer
    17. #9 = Class #27 // superClass: java/lang/Object
    18. #10 = Utf8 c
    19. #11 = Utf8 I
    20. #12 = Utf8 <init>
    21. #13 = Utf8 ()V
    22. #14 = Utf8 Code
    23. #15 = Utf8 LineNumberTable
    24. #16 = Utf8 add
    25. #17 = Utf8 (Ljava/lang/String;)Ljava/lang/String;
    26. #18 = Utf8 SourceFile
    27. #19 = Utf8 ClassViewer.java
    28. #20 = NameAndType #12:#13 // "<init>":()V
    29. #21 = NameAndType #10:#11 // c:I
    30. #22 = Utf8 java/lang/StringBuilder
    31. #23 = NameAndType #28:#29 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    32. #24 = NameAndType #28:#30 // append:(I)Ljava/lang/StringBuilder;
    33. #25 = NameAndType #31:#32 // toString:()Ljava/lang/String;
    34. #26 = Utf8 com/zhz/jvm/ClassViewer
    35. #27 = Utf8 java/lang/Object
    36. #28 = Utf8 append
    37. #29 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
    38. #30 = Utf8 (I)Ljava/lang/StringBuilder;
    39. #31 = Utf8 toString
    40. #32 = Utf8 ()Ljava/lang/String;
    41. {
    42. public com.?.ClassViewer();
    43. descriptor: ()V //构造方法
    44. flags: ACC_PUBLIC //公共
    45. Code:
    46. stack=1, locals=1, args_size=1 // 默认无参构造:使用1个栈Object.init,1个本地变量(全局变量c),1个参数(也是c)
    47. 0: aload_0 // 把局部变量表0位元素,也就是c 加载到操作数栈栈顶
    48. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
    49. 4: return
    50. LineNumberTable:
    51. line 8: 0
    52. public java.lang.String add(java.lang.String);
    53. descriptor: (Ljava/lang/String;)Ljava/lang/String;
    54. flags: ACC_PUBLIC
    55. Code:
    56. stack=3, locals=4, args_size=2 // 本方法调用了3个栈帧,4个局部变量(a,i,j,c),2个参数(a和c)
    57. 0: iconst_0 // 常数0压入操作数栈
    58. 1: istore_2 // 将栈顶元素 保存到局部变量表2的位置
    59. 2: bipush 10 //取值 10 压入栈顶
    60. 4: istore_3 // 栈顶元素 保存到局部变量表index=3的位置
    61. 5: iinc 2, 1 // 取局部变量表index=2的元素,做+1操作
    62. 8: aload_0 //加载局部变量表index=0的值 到栈顶
    63. 9: dup
    64. 10: getfield #2 // Field c:I 获取#2的全局变量值 =0 压入栈顶
    65. 13: iload_3 //加载局部变量表index=3的值到栈顶
    66. 14: iadd //取栈顶两个元素做++操作
    67. 15: putfield #2 // Field c:I 赋值
    68. 18: new #3 // class java/lang/StringBuilder
    69. 21: dup
    70. 22: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
    71. 25: aload_1
    72. 26: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    73. 29: iload_2
    74. 30: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
    75. 33: iload_3
    76. 34: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
    77. 37: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    78. 40: areturn
    79. LineNumberTable:
    80. line 11: 0
    81. line 12: 5
    82. line 13: 8
    83. line 14: 18
    84. }
    85. SourceFile: "ClassViewer.java"

    image.png

    JVM指令

  • load 将局部变量表指定位置的数据 压入 操作数栈 栈顶

  • store 将操作数栈 栈顶元素 保存到局部变量表指定位置
  • iconst/bipush/sipush/ldc 取值指令,根据取值大小用不同指令,?/byte/short/?/int用ldc,
  • iinc 自增
  • invokevisual/invokestatic/invokespecial 调用实例方法/调用静态类方法/调用特殊如构造方法
  • ireturn 返回

    Java ASM框架

    ASM API基于访问者模式,为我们提供了ClassVisitor,MethodVisitor,FieldVisitor API接口,每当ASM扫描到类字段是会回调visitField方法,扫描到类方法是会回调MethodVisitor.

//todo
aop的静态织入,还有spring 使用真实参数名(正常class编译是arg0,arg1),也是asm技术。