- 1、Demo
- 2、Class 文件说明
- 2.1 Class文件结构
- 2.2 jvm type, method signature
- 2.3 泛型表示
- 3、方法说明
- 3.1 方法结构
- 3.1.1 Thread Stack Model
- 3.1.2 指令
- 3.1.3 Stack Map Frames
- 3.1 方法结构
- 4、字节码工具库
- 5、JVM 指令集
在java程序开发过程中,没有源码是很常见的事,我们可以通过IDE(Eclipse需要配置jad.exe、IDEA)自动的将class文件反编译为java文件。或者我们借助于其他工具(jd-gui, luyten)来直接的对整个jar包进行反编译。反编译工具是如何做到代码反编译的呢?如果是在生产环境下帮助用户解决问题时,是没有这些工具的,该怎么办呢?
在开发apm,jprofiler这样的工具时,spring团队在实现aop (例如interceptor),通常都会通过字节码 技术对已有代码进行改造,以达到对代码进行监控,改造工作。那么又是如何将字节码指令嵌入的呢?
要想了解这些东西,不学习class文件如何看,不了解相关字节码指令是很难解决上面的问题的。
1、Demo
在了解class文件之前,先来看一个例子:这是一个真实的需求。不同的机器时间没有使用NTP服务进行时间同步。在这样的一个环境下,各个机器的数据都以入库(如果的数据是机器自身的时间),现在要查看某个机器当前时间的数据,或者最近20分钟的数据。为了解决该问题,做了一个机器时间计算工具:
实现的源码如下:
public class RelativeTime { private String machineId; private long delta; public RelativeTime() { } public RelativeTime(final String machineId, final long time) { this.machineId = machineId; final long now = System.currentTimeMillis(); this.delta = now - time; } public String getMachineId() { return this.machineId; } public void setMachineId(final String machineId) { this.machineId = machineId; } public long getDelta() { return this.delta; } public void setDelta(final long delta) { this.delta = delta; } }
import java.util.concurrent.*; public class RelativeTimeManager { private final ConcurrentHashMapcache; public RelativeTimeManager() { this.cache = new ConcurrentHashMap (); } public void add(final String machineId, final long machineTime) { if (machineId != null) { final RelativeTime t = new RelativeTime(machineId, machineTime); this.add(t); } } public void add(final RelativeTime time) { if (time != null) { this.cache.put(time.getMachineId(), time); } } public void addIfAbsent(final String machineId, final long machineTime) { if (machineId != null && machineTime > 0L) { final RelativeTime t = new RelativeTime(machineId, machineTime); this.addIfAbsent(t); } } public void addIfAbsent(final RelativeTime time) { this.cache.putIfAbsent(time.getMachineId(), time); } public void remove(final String machineId) { if (machineId != null) { this.cache.remove(machineId); } } public boolean hasMachine(final String machineId) { return machineId != null && this.cache.get(machineId) != null; } public long getDeltaTime(final String machineId) { return this.cache.get(machineId).getDelta(); } public long getMachineCurrentTime(final String machineId) { return this.getMachineRelativeTime(machineId, System.currentTimeMillis()); } public long getMachineRelativeTime(final String machineId, final long time) { if (this.hasMachine(machineId)) { final long delta = this.getDeltaTime(machineId); return time - delta; } return time; } }
先来解决上面提出的第一个问题,没有反编译工具的情况下如何查看java类:
在java_home/bin目录下,有这样一个工具:javap,使用它即可查看class文件内容。
对上述两个类分别执行 javap -l -v -p RelativeTime.class (javap -l -v -p RelativeTimeManager.class) 命令后如下:
RelativeTime:
Classfile /C:/Users/User/Desktop/webgate-commons/com/bes/webgate/common/clock/RelativeTime.class Last modified 2017-11-10; size 1005 bytes MD5 checksum eff96db3a12a575b2a4ebc1272b896e4 Compiled from "RelativeTime.java" public class com.bes.webgate.common.clock.RelativeTime SourceFile: "RelativeTime.java" minor version: 0 major version: 50 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#31 // java/lang/Object."":()V #2 = Fieldref #5.#32 // com/bes/webgate/common/clock/RelativeTime.machineId:Ljava/lang/String; #3 = Methodref #33.#34 // java/lang/System.currentTimeMillis:()J #4 = Fieldref #5.#35 // com/bes/webgate/common/clock/RelativeTime.delta:J #5 = Class #36 // com/bes/webgate/common/clock/RelativeTime #6 = Class #37 // java/lang/Object #7 = Utf8 machineId #8 = Utf8 Ljava/lang/String; #9 = Utf8 delta #10 = Utf8 J #11 = Utf8 #12 = Utf8 ()V #13 = Utf8 Code #14 = Utf8 LineNumberTable #15 = Utf8 LocalVariableTable #16 = Utf8 this #17 = Utf8 Lcom/bes/webgate/common/clock/RelativeTime; #18 = Utf8 (Ljava/lang/String;J)V #19 = Utf8 time #20 = Utf8 now #21 = Utf8 getMachineId #22 = Utf8 ()Ljava/lang/String; #23 = Utf8 setMachineId #24 = Utf8 (Ljava/lang/String;)V #25 = Utf8 getDelta #26 = Utf8 ()J #27 = Utf8 setDelta #28 = Utf8 (J)V #29 = Utf8 SourceFile #30 = Utf8 RelativeTime.java #31 = NameAndType #11:#12 // " ":()V #32 = NameAndType #7:#8 // machineId:Ljava/lang/String; #33 = Class #38 // java/lang/System #34 = NameAndType #39:#26 // currentTimeMillis:()J #35 = NameAndType #9:#10 // delta:J #36 = Utf8 com/bes/webgate/common/clock/RelativeTime #37 = Utf8 java/lang/Object #38 = Utf8 java/lang/System #39 = Utf8 currentTimeMillis { private java.lang.String machineId; flags: ACC_PRIVATE private long delta; flags: ACC_PRIVATE public com.bes.webgate.common.clock.RelativeTime(); flags: ACC_PUBLIC LineNumberTable: line 20: 0 line 22: 4 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/bes/webgate/common/clock/RelativeTime; Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object." ":()V 4: return LineNumberTable: line 20: 0 line 22: 4 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/bes/webgate/common/clock/RelativeTime; public com.bes.webgate.common.clock.RelativeTime(java.lang.String, long); flags: ACC_PUBLIC LineNumberTable: line 24: 0 line 25: 4 line 26: 9 line 27: 14 line 28: 22 LocalVariableTable: Start Length Slot Name Signature 0 23 0 this Lcom/bes/webgate/common/clock/RelativeTime; 0 23 1 machineId Ljava/lang/String; 0 23 2 time J 14 9 4 now J Code: stack=5, locals=6, args_size=3 0: aload_0 1: invokespecial #1 // Method java/lang/Object." ":()V 4: aload_0 5: aload_1 6: putfield #2 // Field machineId:Ljava/lang/String; 9: invokestatic #3 // Method java/lang/System.currentTimeMillis:()J 12: lstore 4 14: aload_0 15: lload 4 17: lload_2 18: lsub 19: putfield #4 // Field delta:J 22: return LineNumberTable: line 24: 0 line 25: 4 line 26: 9 line 27: 14 line 28: 22 LocalVariableTable: Start Length Slot Name Signature 0 23 0 this Lcom/bes/webgate/common/clock/RelativeTime; 0 23 1 machineId Ljava/lang/String; 0 23 2 time J 14 9 4 now J public java.lang.String getMachineId(); flags: ACC_PUBLIC LineNumberTable: line 31: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/bes/webgate/common/clock/RelativeTime; Code: stack=1, locals=1, args_size=1 0: aload_0 1: getfield #2 // Field machineId:Ljava/lang/String; 4: areturn LineNumberTable: line 31: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/bes/webgate/common/clock/RelativeTime; public void setMachineId(java.lang.String); flags: ACC_PUBLIC LineNumberTable: line 35: 0 line 36: 5 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Lcom/bes/webgate/common/clock/RelativeTime; 0 6 1 machineId Ljava/lang/String; Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: putfield #2 // Field machineId:Ljava/lang/String; 5: return LineNumberTable: line 35: 0 line 36: 5 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Lcom/bes/webgate/common/clock/RelativeTime; 0 6 1 machineId Ljava/lang/String; public long getDelta(); flags: ACC_PUBLIC LineNumberTable: line 39: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/bes/webgate/common/clock/RelativeTime; Code: stack=2, locals=1, args_size=1 0: aload_0 1: getfield #4 // Field delta:J 4: lreturn LineNumberTable: line 39: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/bes/webgate/common/clock/RelativeTime; public void setDelta(long); flags: ACC_PUBLIC LineNumberTable: line 43: 0 line 44: 5 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Lcom/bes/webgate/common/clock/RelativeTime; 0 6 1 delta J Code: stack=3, locals=3, args_size=2 0: aload_0 1: lload_1 2: putfield #4 // Field delta:J 5: return LineNumberTable: line 43: 0 line 44: 5 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Lcom/bes/webgate/common/clock/RelativeTime; 0 6 1 delta J }
RelativeTimeManager:
Classfile /C:/Users/User/Desktop/webgate-commons/com/bes/webgate/common/clock/RelativeTimeManager.class Last modified 2017-11-10; size 2352 bytes MD5 checksum 3475ce705b7622e212b5630a59413264 Compiled from "RelativeTimeManager.java" public class com.bes.webgate.common.clock.RelativeTimeManager SourceFile: "RelativeTimeManager.java" minor version: 0 major version: 50 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #20.#56 // java/lang/Object."":()V #2 = Class #57 // java/util/concurrent/ConcurrentHashMap #3 = Methodref #2.#56 // java/util/concurrent/ConcurrentHashMap." ":()V #4 = Fieldref #19.#58 // com/bes/webgate/common/clock/RelativeTimeManager.cache:Ljava/util/concurrent/ConcurrentHashMap; #5 = Class #59 // com/bes/webgate/common/clock/RelativeTime #6 = Methodref #5.#60 // com/bes/webgate/common/clock/RelativeTime." ":(Ljava/lang/String;J)V #7 = Methodref #19.#61 // com/bes/webgate/common/clock/RelativeTimeManager.add:(Lcom/bes/webgate/common/clock/RelativeTime;)V #8 = Methodref #5.#62 // com/bes/webgate/common/clock/RelativeTime.getMachineId:()Ljava/lang/String; #9 = Methodref #2.#63 // java/util/concurrent/ConcurrentHashMap.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #10 = Methodref #19.#64 // com/bes/webgate/common/clock/RelativeTimeManager.addIfAbsent:(Lcom/bes/webgate/common/clock/RelativeTime;)V #11 = Methodref #2.#65 // java/util/concurrent/ConcurrentHashMap.putIfAbsent:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #12 = Methodref #2.#66 // java/util/concurrent/ConcurrentHashMap.remove:(Ljava/lang/Object;)Ljava/lang/Object; #13 = Methodref #2.#67 // java/util/concurrent/ConcurrentHashMap.get:(Ljava/lang/Object;)Ljava/lang/Object; #14 = Methodref #5.#68 // com/bes/webgate/common/clock/RelativeTime.getDelta:()J #15 = Methodref #69.#70 // java/lang/System.currentTimeMillis:()J #16 = Methodref #19.#71 // com/bes/webgate/common/clock/RelativeTimeManager.getMachineRelativeTime:(Ljava/lang/String;J)J #17 = Methodref #19.#72 // com/bes/webgate/common/clock/RelativeTimeManager.hasMachine:(Ljava/lang/String;)Z #18 = Methodref #19.#73 // com/bes/webgate/common/clock/RelativeTimeManager.getDeltaTime:(Ljava/lang/String;)J #19 = Class #74 // com/bes/webgate/common/clock/RelativeTimeManager #20 = Class #75 // java/lang/Object #21 = Utf8 cache #22 = Utf8 Ljava/util/concurrent/ConcurrentHashMap; #23 = Utf8 Signature #24 = Utf8 Ljava/util/concurrent/ConcurrentHashMap ; #25 = Utf8 #26 = Utf8 ()V #27 = Utf8 Code #28 = Utf8 LineNumberTable #29 = Utf8 LocalVariableTable #30 = Utf8 this #31 = Utf8 Lcom/bes/webgate/common/clock/RelativeTimeManager; #32 = Utf8 add #33 = Utf8 (Ljava/lang/String;J)V #34 = Utf8 t #35 = Utf8 Lcom/bes/webgate/common/clock/RelativeTime; #36 = Utf8 machineId #37 = Utf8 Ljava/lang/String; #38 = Utf8 machineTime #39 = Utf8 J #40 = Utf8 StackMapTable #41 = Utf8 (Lcom/bes/webgate/common/clock/RelativeTime;)V #42 = Utf8 time #43 = Utf8 addIfAbsent #44 = Utf8 remove #45 = Utf8 (Ljava/lang/String;)V #46 = Utf8 hasMachine #47 = Utf8 (Ljava/lang/String;)Z #48 = Utf8 getDeltaTime #49 = Utf8 (Ljava/lang/String;)J #50 = Utf8 getMachineCurrentTime #51 = Utf8 getMachineRelativeTime #52 = Utf8 (Ljava/lang/String;J)J #53 = Utf8 delta #54 = Utf8 SourceFile #55 = Utf8 RelativeTimeManager.java #56 = NameAndType #25:#26 // " ":()V #57 = Utf8 java/util/concurrent/ConcurrentHashMap #58 = NameAndType #21:#22 // cache:Ljava/util/concurrent/ConcurrentHashMap; #59 = Utf8 com/bes/webgate/common/clock/RelativeTime #60 = NameAndType #25:#33 // " ":(Ljava/lang/String;J)V #61 = NameAndType #32:#41 // add:(Lcom/bes/webgate/common/clock/RelativeTime;)V #62 = NameAndType #76:#77 // getMachineId:()Ljava/lang/String; #63 = NameAndType #78:#79 // put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #64 = NameAndType #43:#41 // addIfAbsent:(Lcom/bes/webgate/common/clock/RelativeTime;)V #65 = NameAndType #80:#79 // putIfAbsent:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #66 = NameAndType #44:#81 // remove:(Ljava/lang/Object;)Ljava/lang/Object; #67 = NameAndType #82:#81 // get:(Ljava/lang/Object;)Ljava/lang/Object; #68 = NameAndType #83:#84 // getDelta:()J #69 = Class #85 // java/lang/System #70 = NameAndType #86:#84 // currentTimeMillis:()J #71 = NameAndType #51:#52 // getMachineRelativeTime:(Ljava/lang/String;J)J #72 = NameAndType #46:#47 // hasMachine:(Ljava/lang/String;)Z #73 = NameAndType #48:#49 // getDeltaTime:(Ljava/lang/String;)J #74 = Utf8 com/bes/webgate/common/clock/RelativeTimeManager #75 = Utf8 java/lang/Object #76 = Utf8 getMachineId #77 = Utf8 ()Ljava/lang/String; #78 = Utf8 put #79 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #80 = Utf8 putIfAbsent #81 = Utf8 (Ljava/lang/Object;)Ljava/lang/Object; #82 = Utf8 get #83 = Utf8 getDelta #84 = Utf8 ()J #85 = Utf8 java/lang/System #86 = Utf8 currentTimeMillis { private final java.util.concurrent.ConcurrentHashMap cache; flags: ACC_PRIVATE, ACC_FINAL Signature: #24 // Ljava/util/concurrent/ConcurrentHashMap ; public com.bes.webgate.common.clock.RelativeTimeManager(); flags: ACC_PUBLIC LineNumberTable: line 22: 0 line 20: 4 line 24: 15 LocalVariableTable: Start Length Slot Name Signature 0 16 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; Code: stack=3, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object." ":()V 4: aload_0 5: new #2 // class java/util/concurrent/ConcurrentHashMap 8: dup 9: invokespecial #3 // Method java/util/concurrent/ConcurrentHashMap." ":()V 12: putfield #4 // Field cache:Ljava/util/concurrent/ConcurrentHashMap; 15: return LineNumberTable: line 22: 0 line 20: 4 line 24: 15 LocalVariableTable: Start Length Slot Name Signature 0 16 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; public void add(java.lang.String, long); flags: ACC_PUBLIC LineNumberTable: line 27: 0 line 28: 4 line 29: 15 line 31: 21 LocalVariableTable: Start Length Slot Name Signature 15 6 4 t Lcom/bes/webgate/common/clock/RelativeTime; 0 22 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 22 1 machineId Ljava/lang/String; 0 22 2 machineTime J Code: stack=5, locals=5, args_size=3 0: aload_1 1: ifnull 21 4: new #5 // class com/bes/webgate/common/clock/RelativeTime 7: dup 8: aload_1 9: lload_2 10: invokespecial #6 // Method com/bes/webgate/common/clock/RelativeTime." ":(Ljava/lang/String;J)V 13: astore 4 15: aload_0 16: aload 4 18: invokevirtual #7 // Method add:(Lcom/bes/webgate/common/clock/RelativeTime;)V 21: return LineNumberTable: line 27: 0 line 28: 4 line 29: 15 line 31: 21 LocalVariableTable: Start Length Slot Name Signature 15 6 4 t Lcom/bes/webgate/common/clock/RelativeTime; 0 22 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 22 1 machineId Ljava/lang/String; 0 22 2 machineTime J StackMapTable: number_of_entries = 1 frame_type = 21 /* same */ public void add(com.bes.webgate.common.clock.RelativeTime); flags: ACC_PUBLIC LineNumberTable: line 34: 0 line 35: 4 line 37: 17 LocalVariableTable: Start Length Slot Name Signature 0 18 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 18 1 time Lcom/bes/webgate/common/clock/RelativeTime; Code: stack=3, locals=2, args_size=2 0: aload_1 1: ifnull 17 4: aload_0 5: getfield #4 // Field cache:Ljava/util/concurrent/ConcurrentHashMap; 8: aload_1 9: invokevirtual #8 // Method com/bes/webgate/common/clock/RelativeTime.getMachineId:()Ljava/lang/String; 12: aload_1 13: invokevirtual #9 // Method java/util/concurrent/ConcurrentHashMap.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; 16: pop 17: return LineNumberTable: line 34: 0 line 35: 4 line 37: 17 LocalVariableTable: Start Length Slot Name Signature 0 18 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 18 1 time Lcom/bes/webgate/common/clock/RelativeTime; StackMapTable: number_of_entries = 1 frame_type = 17 /* same */ public void addIfAbsent(java.lang.String, long); flags: ACC_PUBLIC LineNumberTable: line 40: 0 line 41: 10 line 42: 21 line 44: 27 LocalVariableTable: Start Length Slot Name Signature 21 6 4 t Lcom/bes/webgate/common/clock/RelativeTime; 0 28 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 28 1 machineId Ljava/lang/String; 0 28 2 machineTime J Code: stack=5, locals=5, args_size=3 0: aload_1 1: ifnull 27 4: lload_2 5: lconst_0 6: lcmp 7: ifle 27 10: new #5 // class com/bes/webgate/common/clock/RelativeTime 13: dup 14: aload_1 15: lload_2 16: invokespecial #6 // Method com/bes/webgate/common/clock/RelativeTime." ":(Ljava/lang/String;J)V 19: astore 4 21: aload_0 22: aload 4 24: invokevirtual #10 // Method addIfAbsent:(Lcom/bes/webgate/common/clock/RelativeTime;)V 27: return LineNumberTable: line 40: 0 line 41: 10 line 42: 21 line 44: 27 LocalVariableTable: Start Length Slot Name Signature 21 6 4 t Lcom/bes/webgate/common/clock/RelativeTime; 0 28 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 28 1 machineId Ljava/lang/String; 0 28 2 machineTime J StackMapTable: number_of_entries = 1 frame_type = 27 /* same */ public void addIfAbsent(com.bes.webgate.common.clock.RelativeTime); flags: ACC_PUBLIC LineNumberTable: line 47: 0 line 48: 13 LocalVariableTable: Start Length Slot Name Signature 0 14 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 14 1 time Lcom/bes/webgate/common/clock/RelativeTime; Code: stack=3, locals=2, args_size=2 0: aload_0 1: getfield #4 // Field cache:Ljava/util/concurrent/ConcurrentHashMap; 4: aload_1 5: invokevirtual #8 // Method com/bes/webgate/common/clock/RelativeTime.getMachineId:()Ljava/lang/String; 8: aload_1 9: invokevirtual #11 // Method java/util/concurrent/ConcurrentHashMap.putIfAbsent:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; 12: pop 13: return LineNumberTable: line 47: 0 line 48: 13 LocalVariableTable: Start Length Slot Name Signature 0 14 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 14 1 time Lcom/bes/webgate/common/clock/RelativeTime; public void remove(java.lang.String); flags: ACC_PUBLIC LineNumberTable: line 51: 0 line 52: 4 line 54: 13 LocalVariableTable: Start Length Slot Name Signature 0 14 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 14 1 machineId Ljava/lang/String; Code: stack=2, locals=2, args_size=2 0: aload_1 1: ifnull 13 4: aload_0 5: getfield #4 // Field cache:Ljava/util/concurrent/ConcurrentHashMap; 8: aload_1 9: invokevirtual #12 // Method java/util/concurrent/ConcurrentHashMap.remove:(Ljava/lang/Object;)Ljava/lang/Object; 12: pop 13: return LineNumberTable: line 51: 0 line 52: 4 line 54: 13 LocalVariableTable: Start Length Slot Name Signature 0 14 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 14 1 machineId Ljava/lang/String; StackMapTable: number_of_entries = 1 frame_type = 13 /* same */ public boolean hasMachine(java.lang.String); flags: ACC_PUBLIC LineNumberTable: line 57: 0 line 58: 4 line 60: 6 LocalVariableTable: Start Length Slot Name Signature 0 23 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 23 1 machineId Ljava/lang/String; Code: stack=2, locals=2, args_size=2 0: aload_1 1: ifnonnull 6 4: iconst_0 5: ireturn 6: aload_0 7: getfield #4 // Field cache:Ljava/util/concurrent/ConcurrentHashMap; 10: aload_1 11: invokevirtual #13 // Method java/util/concurrent/ConcurrentHashMap.get:(Ljava/lang/Object;)Ljava/lang/Object; 14: ifnull 21 17: iconst_1 18: goto 22 21: iconst_0 22: ireturn LineNumberTable: line 57: 0 line 58: 4 line 60: 6 LocalVariableTable: Start Length Slot Name Signature 0 23 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 23 1 machineId Ljava/lang/String; StackMapTable: number_of_entries = 3 frame_type = 6 /* same */ frame_type = 14 /* same */ frame_type = 64 /* same_locals_1_stack_item */ stack = [ int ] public long getDeltaTime(java.lang.String); flags: ACC_PUBLIC LineNumberTable: line 64: 0 LocalVariableTable: Start Length Slot Name Signature 0 15 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 15 1 machineId Ljava/lang/String; Code: stack=2, locals=2, args_size=2 0: aload_0 1: getfield #4 // Field cache:Ljava/util/concurrent/ConcurrentHashMap; 4: aload_1 5: invokevirtual #13 // Method java/util/concurrent/ConcurrentHashMap.get:(Ljava/lang/Object;)Ljava/lang/Object; 8: checkcast #5 // class com/bes/webgate/common/clock/RelativeTime 11: invokevirtual #14 // Method com/bes/webgate/common/clock/RelativeTime.getDelta:()J 14: lreturn LineNumberTable: line 64: 0 LocalVariableTable: Start Length Slot Name Signature 0 15 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 15 1 machineId Ljava/lang/String; public long getMachineCurrentTime(java.lang.String); flags: ACC_PUBLIC LineNumberTable: line 68: 0 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 9 1 machineId Ljava/lang/String; Code: stack=4, locals=2, args_size=2 0: aload_0 1: aload_1 2: invokestatic #15 // Method java/lang/System.currentTimeMillis:()J 5: invokevirtual #16 // Method getMachineRelativeTime:(Ljava/lang/String;J)J 8: lreturn LineNumberTable: line 68: 0 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 9 1 machineId Ljava/lang/String; public long getMachineRelativeTime(java.lang.String, long); flags: ACC_PUBLIC LineNumberTable: line 72: 0 line 73: 8 line 74: 15 line 76: 20 LocalVariableTable: Start Length Slot Name Signature 15 5 4 delta J 0 22 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 22 1 machineId Ljava/lang/String; 0 22 2 time J Code: stack=4, locals=6, args_size=3 0: aload_0 1: aload_1 2: invokevirtual #17 // Method hasMachine:(Ljava/lang/String;)Z 5: ifeq 20 8: aload_0 9: aload_1 10: invokevirtual #18 // Method getDeltaTime:(Ljava/lang/String;)J 13: lstore 4 15: lload_2 16: lload 4 18: lsub 19: lreturn 20: lload_2 21: lreturn LineNumberTable: line 72: 0 line 73: 8 line 74: 15 line 76: 20 LocalVariableTable: Start Length Slot Name Signature 15 5 4 delta J 0 22 0 this Lcom/bes/webgate/common/clock/RelativeTimeManager; 0 22 1 machineId Ljava/lang/String; 0 22 2 time J StackMapTable: number_of_entries = 1 frame_type = 20 /* same */ }
2、Class 文件说明
2.1 Class 文件结构
从上面的class文件中,我们就可以看出个大概结构:
从这两个文件的结构中,我们可以轻松的看出一个class文件的结构:
需要注意的是,class文件中,是不会保留代码注释的。如果以非debug模式编译的话,连行号都不会有的。开发过程中的class,都是有行号的,不然都没办法调试了。
2.2 jvm type 、method signature
从上面demo的常量池(Constant pool),可以看到包含了一个类的类型,方法描述,字段描述等。
同时也可以看出类名,方法名,字段名就是和源码一样的,但是字段的类型,方法的类型,和真是的java类型还是有区别的。例如machineId的java类型java.lang.String,而class文件表示的则是 Ljava/lang/String; long表示为J
看来JVM中是有一套自己的表示方式的:
jvm type signature:
于此同时,我编写了一个java类向jvm type signature转换的工具:
Jvm中也有自己的一套method signature:
(paramTypeSignatures)returnTypeSignature
例如:
有了上面的类型签名的转换工具,想必实现方法前面的转换也就不成问题的了。
2.3 泛型表示
Java类型,方法在jvm都有一套自己的表示方式,Java5中引入的泛型,也自然有一套表示方式的,感兴趣的话,可以自己了解一下。
下面是一些简单的例子:
3、方法说明
了解了class文件结构,类型,方法的表示后,下面就进入重头戏了——查看方法。
3.1 方法结构
从上面的字节码,也能看出一个方法会有一堆的指令组成。每一个方法,大概有这些描述区域:
1) modifier(flags:)
2) LineNumberTable (debug模式编译的class文件才会有,反编译工具反编译处理的代码行号之所以对应,你之所以可以调试代码,就是依赖于此)
3) LocalVariableTable (局部变量表,包含三部分内容: this, 方法的所有参数,方法体中声明的所有局部变量)
4) Code (指令序列,运行是就是按照该序列执行的)
5)StackMapTable(stack map frames table,用于jvm在加载类时,verify阶段对code中指令序列里,在所有的跳转指令处,进行execute method stack frame状态校验。目前可能对这句话不理解,不要急,看了后面的内容,就理解了)
3.1.1 Thread Stack Model
如果你稍有Java开发经验,应该都会调试过代码,或者用jstack打过运行时调用栈,或者是看过exception stack。也就是,在运行时采用stack结构各个方法的调串成一条调用链的。在jvm内部,将运行时stack中的每一个元素(方法调用)看作是一个execute frame。
每一个execute frame由两部分组成:一个local variables table,一个operand stack。
Local variables table用于放局部变量,this等。
Operand stack 是执行指令时涉及的操作数存放的地方。
3.1.2 指令
Java运行时的最小单位就是指令的执行。一条完整的指令包括两部分,一个是操作码(opcode),一个是操作数(operand)。
例如:IADD a,b ,这条指令是执行a + b, IADD是 opcode, a,b 是operand。
也就是说operand可以理解为opcode的参数。
指令执行时,通常是对三个地方的数据 进行操作1)local variables table, 2) operand stack, 3) constant pool 。
Jvm 提供了很多指令,这些指令大概可以分为两类:
1) 从local variables table 或者 constants pool 将值load到 operand stack,或者反过来将 operand stack 中的值 store 给local variables table。
2) 基于stack 进行操作,例如 IADD, 就从 stack 顶取2个值进行 加法运算后,将结果push到stack。
到此,相信可以大概猜测出运行过程 :执行一个方法时:
1) 构建 execute frame (初始化 local variables table, operand stack)
2) 将execute frame 放到 thread stack。
在构建local variables table时,第一个(也就是index 为0的)是this, 后面紧接的是方法的参数列表。
在执行方法内的指令序列时,遇见 load类指令时,就load数据到operand stack,遇见运算类指令时,就从 operand stack 中取值运行,遇见 store类指令时,就从stack顶取值放到local variables table中。
3.1.3 Stack Map Frames
从Java 6起,引入了使用stack map frames进行字节码校验机制。那么stack map frames到底是怎样一回事呢?
假设你的类,被人篡改了,加入了一些不可描述的指令。通过jvm 的stack map frames检验,是可以帮你查出来问题的。
上面已经说明了operand stack,也了解了指令执行过程,可以说大多数指令,都是要与operand stack打交道的。这个检验就是基于operand stack在执行指令前后的状态来的。
例如:
上面 就是一个stack map。也就是说记录了指令执行前后,operand stack 与 指令的映射关系的表,就是 stack map frames。
一个方法可以很简单,也可以很复杂。对于一个复杂的方法,指令会非常多,那么它的stack map frames 会非常长。如果类加载过程中,要校验这么长的stack map,那性能可想而知。
所以JVM设计者呢,采取了一个折中的方案。只在stack map frames 里只保留跳转指令的状态。至于跳转指令,例如:if,else,try,catch等。
4、字节码工具库
目前常用的字节码操作的工具有:javaassist,asm,bcel
基于asm的库有bytebuddy,cglib
5、指令集
在上面说了,指令主要针对local variables table, operand stack, constants进行操作。如果详细对指令进行归类的话,大概可以归类如下:
1) Local variables
2) stack
3) Constants
4) Arithmetic and logic
5) Object,field,method
6) Array
7) Jump
8) Return