JVM 指令
- 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();View Codethis.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 ConcurrentHashMap<String, RelativeTime> cache; public RelativeTimeManager() { this.cache = new ConcurrentHashMap<String, RelativeTime>(); } 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; } }View Code
先來解決上面提出的第一個問題,沒有反編譯工具的情況下如何查看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."<init>":()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 <init> #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 // "<init>":()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."<init>":()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."<init>":()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 }View Code
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."<init>":()V #2 = Class #57 // java/util/concurrent/ConcurrentHashMap #3 = Methodref #2.#56 // java/util/concurrent/ConcurrentHashMap."<init>":()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."<init>":(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<Ljava/lang/String;Lcom/bes/webgate/common/clock/RelativeTime;>; #25 = Utf8 <init> #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 // "<init>":()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 // "<init>":(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<java.lang.String, com.bes.webgate.common.clock.RelativeTime> cache; flags: ACC_PRIVATE, ACC_FINAL Signature: #24 // Ljava/util/concurrent/ConcurrentHashMap<Ljava/lang/String;Lcom/bes/webgate/common/clock/RelativeTime;>; 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."<init>":()V 4: aload_0 5: new #2 // class java/util/concurrent/ConcurrentHashMap 8: dup 9: invokespecial #3 // Method java/util/concurrent/ConcurrentHashMap."<init>":()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."<init>":(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."<init>":(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 */ }View Code
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
JVM 指令