1. 程式人生 > >JVM 指令

JVM 指令

opened pri min last 跳轉 cas flags 不理解 註釋

  • 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
  • 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; } }
View Code 技術分享圖片
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 指令