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

 

posted @ 2018-03-07 20:40  乐享程序员  阅读(852)  评论(0编辑  收藏  举报