Java字節碼操縱框架ASM小試


本文主要內容:
ASM是什么 JVM指令
Java字節碼文件
ASM編程模型
ASM示例
參考資料匯總
JVM詳細指令

ASM是什么

ASM是一個Java字節碼操縱框架,它能被用來動態生成類或者增強既有類的功能。ASM可以直接產生二進制class文件,也可以在類被加載入Java虛擬機之前動態改變類行為。Java class被存儲在嚴格格式定義的.class文件里,這些類文件擁有足夠的元數據來解析類中的所有元素:類名稱、方法、屬性以及 Java 字節碼(指令)。ASM從類文件中讀入信息后,能夠改變類行為,分析類信息,甚至能夠根據用戶要求生成新類。

目前許多框架如cglib、Hibernate、Spring都直接或間接地使用ASM操作字節碼,有些語言如Jython、JRuby、Groovy也是如此。而類ASM字節碼工具還有:

  1. BCEL:Byte Code Engineering Library (BCEL),這是Apache Software Foundation 的Jakarta 項目的一部分。BCEL是 Java classworking 最廣泛使用的一種框架,它可以讓您深入 JVM 匯編語言進行類操作的細節。BCEL與Javassist 有不同的處理字節碼方法,BCEL在實際的JVM 指令層次上進行操作(BCEL擁有豐富的JVM 指令級支持)而Javassist 所強調的源代碼級別的工作。
  2. JBET:通過JBET(Java Binary Enhancement Tool )的API可對Class文件進行分解,重新組合,或被編輯。JBET也可以創建新的Class文件。JBET用一種結構化的方式來展現Javabinary (.class)文件的內容,並且可以很容易的進行修改。
  3. Javassist:Javassist是一個開源的分析、編輯和創建Java字節碼的類庫。是由東京技術學院的數學和計算機科學系的 Shigeru Chiba 所創建的。它已加入了開放源代碼JBoss 應用服務器項目,通過使用Javassist對字節碼操作為JBoss實現動態AOP框架。
  4. cglib:是一個強大的,高性能,高質量的Code生成類庫。它可以在運行期擴展Java類與實現Java接口,cglib封裝了asm,可以在運行期動態生成新的 class,Hibernate和Spring都用到過它。cglib用於AOP,jdk中的proxy必須基於接口,cglib卻沒有這個限制。

而ASM與cglib、serp和BCEL相比,ASM有以下的優點 :

  • ASM 具有簡單、設計良好的 API,這些 API 易於使用;
  • ASM 有非常良好的開發文檔,以及可以幫助簡化開發的 Eclipse 插件;
  • ASM 支持 Java 6(ASM3)、Java7(ASM4)、Java(ASM5);
  • ASM 很小、很快、很健壯;
  • ASM 有很大的用戶群,可以幫助新手解決開發過程中遇到的問題;
  • ASM 的開源許可可以讓你幾乎以任何方式使用它;

JVM指令

如果使用ASM框架,需要對JVM指令和Java字節碼文件的結構都需要有點概念。JVM指令總結如下(詳細看參考本文底部的PS)

  1. 凡是帶const的表示將什么數據壓操作數棧;如:
    iconst_2 將int型數據2壓入到操作數棧;
    aconst_null  將null值壓入棧;
  2. bipush和sipush  表示將單字節或者短整形的常量值壓入操作數棧;
  3. 帶ldc的表示將什么類型數據從常量池中壓入到操作數棧;如:
    ldc_w  將int或者flat或者string類型的數據壓入到操作數棧;
    ldc2_w  將long或者double類型的數據壓入到操作數棧;
  4. 凡是帶load的指令表示將某類型的局部變量數據壓入到操作數棧的棧頂;如:
    iload 表示將int類型的局部變量壓入到操作數棧的棧頂;
    aload  以a開頭的表示將引用類型的局部變量壓入到操作數棧的棧頂;
    iload_1 將局部變量數組里面下標為1的int類型的數據壓入到操作數棧;
    iaload   將int型數組的指定索引的值壓入到操作數棧;
  5. 凡是帶有store指令的表示將操作數棧頂的某類型的值存入指定的局部變量中;如:
    istore  表示將棧頂int類型的數據存入到指定的局部變量中;
    istore_3  表示將棧int類型的數據存入到局部變量數組的下標為3的元素中;
  6. pop  將棧頂數據彈出;pop2將棧頂的一個long或者double數據從棧頂彈出來;
  7. dup  復制棧頂的數據並將復制的值也壓入到棧頂;
    dup2  復制棧頂一個long或者是double的數據並將復制的值也壓入到棧頂;
  8. swap  將棧最頂端的兩個值互換;
  9. iadd 將棧頂兩個int型的數據相加然后將結果再次的壓入到棧頂;
    isub 將棧頂兩個int型的數據相減然后將結果再次的壓入到棧頂;   
    imul 將棧頂兩個int型的數據相乘然后將結果再次的壓入到棧頂;
    idiv  將棧頂兩個int型的數據相除然后將結果再次的壓入到棧頂;
    irem 將棧頂兩個int型的數據取模運算然后將結果再次的壓入到棧頂;
    ineg 將棧頂的int數據取負將結果壓入到棧頂;
    iinc  將指定的int變量增加指定值(i++,i--,i+=2);
    i2l   將棧頂int類型數據強制轉換成long型將結果壓入到棧頂;
    lcmp  將棧頂兩long型數據的大小進行比較,並將結果(1,0,-1)壓入棧頂;
  10. 以if開頭的指令都是跳轉指令;
  11. tableswitch、lookupswitch  表示用switch條件跳轉;
  12. ireturn  從當前方法返回int型數據;
  13. getstatic  獲取指定類的靜態域,將將結果壓入到棧頂;
    putstatic 為指定的類的靜態域賦值;
    getfield   獲取指定類的實例變量,將結果壓入到棧頂;
    putfield   為指定類的實例變量賦值;
    invokevirtual  調用實例方法;
    invokespacial  調用超類構造方法,實例初始化方法,私有方法;
    invokestatic  調用靜態方法;
    invokeinterface  調用接口方法; 
    new 創建一個對象,並將其引用壓入到棧頂;
    newarray  創建一個原始類型的數組,並將其引用壓入到棧頂;
    arraylength   獲得一個數組的長度,將將結果壓入到棧頂;
    athrow   將棧頂的異常拋出;
    checkcast  檢驗類型轉換,轉換未通過,將拋出ClassCastException.
    instanceof 檢驗對象是否是指定的類的實例,如果是將1壓入棧頂,否則將0壓入棧頂 
    monitorenter   獲得對象的鎖,用於同步方法或同步塊  
    monitorexit    釋放對象的鎖,用於同步方法或同步塊
    ifnull    為null時跳轉 
    ifnonnull   不為null時跳轉

Java字節碼文件

所謂 Java 字節碼文件,就是通常用 javac 編譯器產生的 .class 文件。這些文件具有嚴格定義的格式。為了更好的理解 ASM,首先對 Java 字節碼文件格式作一點簡單的介紹。Java 源文件經過 javac 編譯器編譯之后,將會生成對應的二進制文件(如下圖所示)。每個合法的 Java 字節碼文件都具備精確的定義,而正是這種精確的定義,才使得 Java 虛擬機得以正確讀取和解釋所有的 Java 字節碼文件。

Java 字節碼文件是 8 位字節的二進制流。數據項按順序存儲在 class 文件中,相鄰的項之間沒有間隔,這使得 class 文件變得緊湊,減少存儲空間。在 Java 字節碼文件中包含了許多大小不同的項,由於每一項的結構都有嚴格規定,這使得 class 文件能夠從頭到尾被順利地解析。下面讓我們來看一下 Java 字節碼文件的內部結構,以便對此有個大致的認識。

例如,一個最簡單的 Hello World 程序:

  1. public class HelloWorld {
  2. public static void main(String[] args) {
  3. System.out.println("Hello world");
  4. }
  5. }

從上圖中可以看到,一個 Java 字節碼文件大致可以歸為 10 個項:

  • Magic:該項存放了一個 Java 字節碼文件的魔數(magic number)和版本信息。一個 Java 字節碼文件的前 4 個字節被稱為它的魔數。每個正確的 Java 字節碼文件都是以 0xCAFEBABE 開頭的,這樣保證了 Java 虛擬機能很輕松的分辨出 Java 文件和非 Java 文件。
  • Version:該項存放了 Java 字節碼文件的版本信息,它對於一個 Java 文件具有重要的意義。因為 Java 技術一直在發展,所以字節碼文件的格式也處在不斷變化之中。字節碼文件的版本信息讓虛擬機知道如何去讀取並處理該字節碼文件。
  • Constant Pool:該項存放了類中各種文字字符串、類名、方法名和接口名稱、final 變量以及對外部類的引用信息等常量。虛擬機必須為每一個被裝載的類維護一個常量池,常量池中存儲了相應類型所用到的所有類型、字段和方法的符號引用,因此它在 Java 的動態鏈接中起到了核心的作用。常量池的大小平均占到了整個類大小的 60% 左右。
  • Access_flag:該項指明了該文件中定義的是類還是接口(一個 class 文件中只能有一個類或接口),同時還指名了類或接口的訪問標志,如 public,private, abstract 等信息。
  • This Class:指向表示該類全限定名稱的字符串常量的指針。
  • Super Class:指向表示父類全限定名稱的字符串常量的指針。
  • Interfaces:一個指針數組,存放了該類或父類實現的所有接口名稱的字符串常量的指針。以上三項所指向的常量,特別是前兩項,在我們用 ASM 從已有類派生新類時一般需要修改:將類名稱改為子類名稱;將父類改為派生前的類名稱;如果有必要,增加新的實現接口。
  • Fields:該項對類或接口中聲明的字段進行了細致的描述。需要注意的是,fields 列表中僅列出了本類或接口中的字段,並不包括從超類和父接口繼承而來的字段。
  • Methods:該項對類或接口中聲明的方法進行了細致的描述。例如方法的名稱、參數和返回值類型等。需要注意的是,methods 列表里僅存放了本類或本接口中的方法,並不包括從超類和父接口繼承而來的方法。使用 ASM 進行 AOP 編程,通常是通過調整 Method 中的指令來實現的。
  • Class attributes:該項存放了在該文件中類或接口所定義的屬性的基本信息。

事實上,使用 ASM 動態生成類,不需要像早年的 class hacker 一樣,熟知 class 文件的每一段,以及它們的功能、長度、偏移量以及編碼方式。ASM 會給我們照顧好這一切的,我們只要告訴 ASM 要改動什么就可以了 —— 當然,我們首先得知道要改什么:對字節碼文件格式了解的越多,我們就能更好地使用 ASM 這個利器。

ASM編程模型

ASM 提供了兩種編程模型:

  • Core API,提供了基於事件形式的編程模型。該模型不需要一次性將整個類的結構讀取到內存中,因此這種方式更快,需要更少的內存。但這種編程方式難度較大。
  • Tree API,提供了基於樹形的編程模型。該模型需要一次性將一個類的完整結構全部讀取到內存當中,所以這種方法需要更多的內存。這種編程方式較簡單。

Core API 中操縱字節碼的功能基於 ClassVisitor 接口。這個接口中的每個方法對應了 class 文件中的每一項。Class 文件中的簡單項的訪問使用一個單獨的方法,方法參數描述了這個項的內容。而那些具有任意長度和復雜度的項,使用另外一類方法,這類方法會返回一個輔助的 Visitor 接口,通過這些輔助接口的對象來完成具體內容的訪問。例如 visitField 方法和 visitMethod 方法,分別返回 FieldVisitor 和 MethodVisitor 接口的對象。

ASM 提供了三個基於 ClassVisitor 接口的類來實現 class 文件的生成和轉換:

  • ClassReader:ClassReader 解析一個類的 class 字節碼,該類的 accept 方法接受一個 ClassVisitor 的對象,在 accept 方法中,會按上文描述的順序逐個調用 ClassVisitor 對象的方法。它可以被看做事件的生產者。
  • ClassAdapter:ClassAdapter 是 ClassVisitor 的實現類。它的構造方法中需要一個 ClassVisitor 對象,並保存為字段 protected ClassVisitor cv。在它的實現中,每個方法都是原封不動的直接調用 cv 的對應方法,並傳遞同樣的參數。可以通過繼承 ClassAdapter 並修改其中的部分方法達到過濾的作用。它可以看做是事件的過濾器。
  • ClassWriter:ClassWriter 也是 ClassVisitor 的實現類。ClassWriter 可以用來以二進制的方式創建一個類的字節碼。對於 ClassWriter 的每個方法的調用會創建類的相應部分。例如:調用 visit 方法就是創建一個類的聲明部分,每調用一次 visitMethod 方法就會在這個類中創建一個新的方法。在調用 visitEnd 方法后即表明該類的創建已經完成。它最終生成一個字節數組,這個字節數組中包含了一個類的 class 文件的完整字節碼內容 。可以通過 toByteArray 方法獲取生成的字節數組。ClassWriter 可以看做事件的消費者。

通常情況下,它們是組合起來使用的。

ASM示例

項目結構如下:

HelloWorld.java代碼如下:

  1. package net.oseye.demoasm;
  2.  
  3. public class HelloWorld {
  4. public void sayHello() {
  5. System.out.println("Hello World!");
  6. }
  7. }

如果我們想動態地在HelloWorld.java的sayHello方法中加入打印時間如:

  1. package net.oseye.demoasm;
  2.  
  3. public class HelloWorld {
  4. public void sayHello() {
  5. System.out.println(System.currentTimeMillis());
  6. System.out.println("Hello World!");
  7. }
  8. }

怎么做呢?

直接編碼ASM其實對於新手來說是很困難的事,但幸運的是ASM給我們提供了ASMifer工具。一般我們會使用ASM的ASMifer工具生成ASM結構來對比,使用命令:

  1. java org.objectweb.asm.util.ASMifier net.oseye.demoasm.HelloWorld

記得"asm-util-x.x.jar"需要在classpath中,如果沒有記得設置classpath,生成沒加入打印時間的HelloWorld.Class的ASM結構如下:

  1. package asm.net.oseye.demoasm;
  2. import java.util.*;
  3. import org.objectweb.asm.*;
  4. import org.objectweb.asm.attrs.*;
  5. public class HelloWorldDump implements Opcodes {
  6.  
  7. public static byte[] dump () throws Exception {
  8.  
  9. ClassWriter cw = new ClassWriter(0);
  10. FieldVisitor fv;
  11. MethodVisitor mv;
  12. AnnotationVisitor av0;
  13.  
  14. cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "net/oseye/demoasm/HelloWorld", null, "ja
  15. va/lang/Object", null);
  16.  
  17. {
  18. mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
  19. mv.visitCode();
  20. mv.visitVarInsn(ALOAD, 0);
  21. mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
  22. mv.visitInsn(RETURN);
  23. mv.visitMaxs(1, 1);
  24. mv.visitEnd();
  25. }
  26. {
  27. mv = cw.visitMethod(ACC_PUBLIC, "sayHello", "()V", null, null);
  28. mv.visitCode();
  29. mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")
  30. ;
  31. mv.visitLdcInsn("Hello World!");
  32. mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang
  33. /String;)V");
  34. mv.visitInsn(RETURN);
  35. mv.visitMaxs(2, 1);
  36. mv.visitEnd();
  37. }
  38. cw.visitEnd();
  39.  
  40. return cw.toByteArray();
  41. }
  42. }

而加入打印時間的ASM結構如下:

  1. package asm.net.oseye.demoasm;
  2. import java.util.*;
  3. import org.objectweb.asm.*;
  4. import org.objectweb.asm.attrs.*;
  5. public class HelloWorldDump implements Opcodes {
  6.  
  7. public static byte[] dump () throws Exception {
  8.  
  9. ClassWriter cw = new ClassWriter(0);
  10. FieldVisitor fv;
  11. MethodVisitor mv;
  12. AnnotationVisitor av0;
  13.  
  14. cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "net/oseye/demoasm/HelloWorld", null, "ja
  15. va/lang/Object", null);
  16.  
  17. {
  18. mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
  19. mv.visitCode();
  20. mv.visitVarInsn(ALOAD, 0);
  21. mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
  22. mv.visitInsn(RETURN);
  23. mv.visitMaxs(1, 1);
  24. mv.visitEnd();
  25. }
  26. {
  27. mv = cw.visitMethod(ACC_PUBLIC, "sayHello", "()V", null, null);
  28. mv.visitCode();
  29. mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")
  30. ;
  31. mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J")
  32. ;
  33. mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(J)V");
  34. mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")
  35. ;
  36. mv.visitLdcInsn("Hello World!");
  37. mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang
  38. /String;)V");
  39. mv.visitInsn(RETURN);
  40. mv.visitMaxs(3, 1);
  41. mv.visitEnd();
  42. }
  43. cw.visitEnd();
  44.  
  45. return cw.toByteArray();
  46. }
  47. }

對比我們發現后者比前者多了:

  1. mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")
  2. ;
  3. mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J")
  4. ;
  5. mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(J)V");

因此App.java可以這樣編碼:

  1. package net.oseye.demoasm;
  2.  
  3. import java.io.IOException;
  4. import java.lang.reflect.InvocationTargetException;
  5.  
  6. import org.objectweb.asm.ClassReader;
  7. import org.objectweb.asm.ClassVisitor;
  8. import org.objectweb.asm.ClassWriter;
  9. import org.objectweb.asm.MethodVisitor;
  10. import org.objectweb.asm.Opcodes;
  11.  
  12. public class App extends ClassLoader implements Opcodes {
  13. public static void main(String[] args) throws IOException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException {
  14. ClassReader cr=new ClassReader(HelloWorld.class.getName());
  15. ClassWriter cw=new ClassWriter(ClassWriter.COMPUTE_MAXS);
  16. CustomVisitor myv=new CustomVisitor(Opcodes.ASM4,cw);
  17. cr.accept(myv, 0);
  18. byte[] code=cw.toByteArray();
  19. //自定義加載器
  20. App loader=new App();
  21. Class<?> appClass=loader.defineClass(null, code, 0,code.length);
  22. appClass.getMethods()[0].invoke(appClass.newInstance(), new Object[]{});
  23. // FileOutputStream f=new FileOutputStream(new File("d:"+File.separator+"ok2.class"));
  24. // f.write(code);;
  25. // f.close();
  26. }
  27. }
  28.  
  29.  
  30. /**
  31. * ClassVisitor的實現類
  32. * App.java:demoasm
  33. * Jul 17, 2014
  34. * @author kevin.zhai
  35. */
  36. class CustomVisitor extends ClassVisitor implements Opcodes {
  37.  
  38. public CustomVisitor(int api, ClassVisitor cv) {
  39. super(api, cv);
  40. }
  41.  
  42. @Override
  43. public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
  44. MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
  45. if (name.equals("sayHello")) {
  46. mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
  47. mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J");
  48. mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(J)V");
  49. }
  50. return mv;
  51. }
  52. }

運行可以看到類似這樣的輸出:

1405587042484
Hello World!

當然你也可以把通過ASM生成的class保存到磁盤然后加載。

PS:

    1. 參考資料匯總: 
      AOP 的利器:ASM 3.0 介紹
      使用 ASM 實現 Java 語言的“多重繼承”
      ASM3 0指南翻譯
      Java字節碼(.class文件)格式詳解
      JVM指令集(指令碼、助記符、功能描述)
      jvm指令集理解
      asm4-guide
    2. JVM詳細指令

      指令碼

      助記符

      功能描述

      0x00

      nop

      無操作

       

      0x01

      aconst_null

       

      指令格式:  aconst_null

       

      功能描述:  null進棧。

       

      指令執行前

      指令執行后

      棧底

      ...

      ...

       

      null

      棧頂

       

      注意:JVM並沒有為null指派一個具體的值。

       

       

      0x02

      iconst_m1

      int型常量值-1進棧

      0x03

      iconst_0

      int型常量值0進棧

      0x04

      iconst_1

      int型常量值1進棧

      0x05

      iconst_2

      int型常量值2進棧

      0x06

      iconst_3

      int型常量值3進棧

      0x07

      iconst_4

      int型常量值4進棧

      0x08

      iconst_5

      int型常量值5進棧

       

      0x09

      lconst_0

      long型常量值0進棧

      0x0A

      lconst_1

      long型常量值1進棧

       

      0x0B

      fconst_0

      float型常量值0進棧

      0x0C

      fconst_1

      float型常量值1進棧

      0x0D

      fconst_2

      float型常量值2進棧

       

      0x0E

      dconst_0

      double型常量值0進棧

      0x0F

      dconst_1

      double型常量值1進棧

       

      0x10

      bipush

      將一個byte型常量值推送至棧頂

      0x11

      sipush

      將一個short型常量值推送至棧頂

       

      0x12

      ldc

      將int、float或String型常量值從常量池中推送至棧頂

      0x13

      ldc_w

      將int、float或String型常量值從常量池中推送至棧頂(寬索引)

      0x14

      ldc2_w

      將long或double型常量值從常量池中推送至棧頂(寬索引)

       

      0x15

      iload

      指定的int型局部變量進棧

      0x16

      lload

      指定的long型局部變量進棧

      0x17

      fload

      指定的float型局部變量進棧

      0x18

      dload

      指定的double型局部變量進棧

      0x19

      aload

       

      指令格式:  aload index

       

      功能描述:  當前frame的局部變量數組中下標為

                 index的引用型局部變量進棧

       

      指令執行前

      指令執行后

      棧底

      ...

      ...

       

      objectref

      棧頂

       

      index  :  無符號一byte整型。和wide指令聯用,

                 可以使index為兩byte。

       

       

      0x1A

      iload_0

      第一個int型局部變量進棧

      0x1B

      iload_1

      第二個int型局部變量進棧

      0x1C

      iload_2

      第三個int型局部變量進棧

      0x1D

      iload_3

      第四個int型局部變量進棧

       

      0x1E

      lload_0

      第一個long型局部變量進棧

      0x1F

      lload_1

      第二個long型局部變量進棧

      0x20

      lload_2

      第三個long型局部變量進棧

      0x21

      lload_3

      第四個long型局部變量進棧

       

      0x22

      fload_0

      第一個float型局部變量進棧

      0x23

      fload_1

      第二個float型局部變量進棧

      0x24

      fload_2

      第三個float型局部變量進棧

      0x25

      fload_3

      第四個float型局部變量進棧

       

      0x26

      dload_0

      第一個double型局部變量進棧

      0x27

      dload_1

      第二個double型局部變量進棧

      0x28

      dload_2

      第三個double型局部變量進棧

      0x29

      dload_3

      第四個double型局部變量進棧

       

      0x2A

      aload_0

       

      指令格式:aload_0

       

      該指令的行為類似於aload指令index為0的情況。

       

      0x2B

      aload_1

       

      同上

       

      0x2C

      aload_2

       

      同上

       

      0x2D

      aload_3

       

      同上

       

       

      0x2E

      iaload

      指定的int型數組的指定下標處的值進棧

      0x2F

      laload

      指定的long型數組的指定下標處的值進棧

      0x30

      faload

      指定的float型數組的指定下標處的值進棧

      0x31

      daload

      指定的double型數組的指定下標處的值進棧

      0x32

      aaload

       

      指令格式:  aaload

       

      功能描述:  棧頂的數組下標(index)、數組引用

                 (arrayref)出棧,並根據這兩個數值

                 取出對應的數組元素值(value)進棧。

       

      拋出異常:  如果arrayref的值為null,會拋出

                 NullPointerException。

                 如果index造成數組越界,會拋出

                 ArrayIndexOutOfBoundsException。

       

      指令執行前

      指令執行后

      棧底

      ...

      ...

      arrayref

      value

      index

       

      棧頂

       

      index      :  int類型

      arrayref   :  數組的引用

       

      0x33

      baload

      指定的boolean或byte型數組的指定下標處的值進棧

      0x34

      caload

      指定的char型數組的指定下標處的值進棧

      0x35

      saload

      指定的short型數組的指定下標處的值進棧

       

      0x36

      istore

      將棧頂int型數值存入指定的局部變量

      0x37

      lstore

      將棧頂long型數值存入指定的局部變量

      0x38

      fstore

      將棧頂float型數值存入指定的局部變量

      0x39

      dstore

      將棧頂double型數值存入指定的局部變量

      0x3A

      astore

       

      指令格式:  astore index

       

      功能描述:  將棧頂數值(objectref)存入當前

                 frame的局部變量數組中指定下標

                 (index)處的變量中,棧頂數值出棧。

       

      指令執行前

      指令執行后

      棧底

      ...

      ...

      objectref

       

      棧頂

       

      index  :  無符號一byte整數。該指令和wide聯

                 用,index可以為無符號兩byte整數。

       

       

      0x3B

      istore_0

      將棧頂int型數值存入第一個局部變量

      0x3C

      istore_1

      將棧頂int型數值存入第二個局部變量

      0x3D

      istore_2

      將棧頂int型數值存入第三個局部變量

      0x3E

      istore_3

      將棧頂int型數值存入第四個局部變量

       

      0x3F

      lstore_0

      將棧頂long型數值存入第一個局部變量

      0x40

      lstore_1

      將棧頂long型數值存入第二個局部變量

      0x41

      lstore_2

      將棧頂long型數值存入第三個局部變量

      0x42

      lstore_3

      將棧頂long型數值存入第四個局部變量

       

      0x43

      fstore_0

      將棧頂float型數值存入第一個局部變量

      0x44

      fstore_1

      將棧頂float型數值存入第二個局部變量

      0x45

      fstore_2

      將棧頂float型數值存入第三個局部變量

      0x46

      fstore_3

      將棧頂float型數值存入第四個局部變量

       

      0x47

      dstore_0

      將棧頂double型數值存入第一個局部變量

      0x48

      dstore_1

      將棧頂double型數值存入第二個局部變量

      0x49

      dstore_2

      將棧頂double型數值存入第三個局部變量

      0x4A

      dstore_3

      將棧頂double型數值存入第四個局部變量

       

      0x4B

      astore_0

       

      指令格式:  astore_0

       

      功能描述:  該指令的行為類似於astore指令index

                 為0的情況。

       

      0x4C

      astore_1

       

      同上

       

      0x4D

      astore_2

       

      同上

       

      0x4E

      astore_3

       

      同上

       

       

      0x4F

      iastore 

      將棧頂int型數值存入指定數組的指定下標處

      0x50

      lastore

      將棧頂long型數值存入指定數組的指定下標處

      0x51

      fastore

      將棧頂float型數值存入指定數組的指定下標處

      0x52

      dastore

      將棧頂double型數值存入指定數組的指定下標處

      0x53

      aastore

       

      指令格式:  aastore

       

      功能描述:  根據棧頂的引用型數值(value)、數組下

                 標(index)、數組引用(arrayref)出

                 棧,將數值存入對應的數組元素中。

       

      拋出異常:  如果value的類型和arrayref所引用

                 的數組的元素類型不兼容,會拋出拋出

                 ArrayStoreException。

                 如果index造成數組越界,會拋出

                 ArrayIndexOutOfBoundsException。

                 如果arrayref值為null,會拋出

                 NullPointerException。

       

      指令執行前

      指令執行后

      棧底

      ...

      ...

      arrayref

       

      index

       

      value

       

      棧頂

       

      arrayref   :  必須是對數組的引用

      index      :  int類型

      value      :  引用類型

       

      0x54

      bastore

      將棧頂boolean或byte型數值存入指定數組的指定下標處

      0x55

      castore

      將棧頂char型數值存入指定數組的指定下標處

      0x56

      sastore

      將棧頂short型數值存入指定數組的指定下標處

       

      0x57

      pop

      棧頂數值出棧 (該棧頂數值不能是long或double型)

      0x58

      pop2

      棧頂的一個(如果是long、double型的)或兩個(其它類型的)數值出棧

       

      0x59

      dup

      復制棧頂數值,並且復制值進棧

      0x5A

      dup_x1

      復制棧頂數值,並且復制值進棧2次

      0x5B

      dup_x2

      復制棧頂數值,並且復制值進棧2次或3次

      0x5C

      dup2

      復制棧頂一個(long、double型的)或兩個(其它類型的)數值,並且復制值進棧

      0x5D

      dup2_x1

       

      0x5E

      dup2_x2

       

       

      0x5F

      swap

      棧頂的兩個數值互換(要求棧頂的兩個數值不能是long或double型的)

       

      0x60

      iadd

      棧頂兩int型數值相加,並且結果進棧

      0x61

      ladd

      棧頂兩long型數值相加,並且結果進棧

      0x62

      fadd

      棧頂兩float型數值相加,並且結果進棧

      0x63

      dadd

      棧頂兩double型數值相加,並且結果進棧

       

      0x64

      isub

      棧頂兩int型數值相減,並且結果進棧

      0x65

      lsub

      棧頂兩long型數值相減,並且結果進棧

      0x66

      fsub

      棧頂兩float型數值相減,並且結果進棧

      0x67

      dsub

      棧頂兩double型數值相減,並且結果進棧

       

      0x68

      imul

      棧頂兩int型數值相乘,並且結果進棧

      0x69

      lmul

      棧頂兩long型數值相乘,並且結果進棧

      0x6A

      fmul

      棧頂兩float型數值相乘,並且結果進棧

      0x6B

      dmul

      棧頂兩double型數值相乘,並且結果進棧

       

      0x6C

      idiv

      棧頂兩int型數值相除,並且結果進棧

      0x6D

      ldiv

      棧頂兩long型數值相除,並且結果進棧

      0x6E

      fdiv

      棧頂兩float型數值相除,並且結果進棧

      0x6F

      ddiv

      棧頂兩double型數值相除,並且結果進棧

       

      0x70

      irem

      棧頂兩int型數值作取模運算,並且結果進棧

      0x71

      lrem

      棧頂兩long型數值作取模運算,並且結果進棧

      0x72

      frem

      棧頂兩float型數值作取模運算,並且結果進棧

      0x73

      drem

      棧頂兩double型數值作取模運算,並且結果進棧

       

      0x74

      ineg

      棧頂int型數值取負,並且結果進棧

      0x75

      lneg

      棧頂long型數值取負,並且結果進棧

      0x76

      fneg

      棧頂float型數值取負,並且結果進棧

      0x77

      dneg

      棧頂double型數值取負,並且結果進棧

       

      0x78

      ishl

      int型數值左移指定位數,並且結果進棧

      0x79

      lshl

      long型數值左移指定位數,並且結果進棧

       

      0x7A

      ishr

      int型數值帶符號右移指定位數,並且結果進棧

      0x7B

      lshr

      long型數值帶符號右移指定位數,並且結果進棧

      0x7C

      iushr

      int型數值無符號右移指定位數,並且結果進棧

      0x7D

      lushr

      long型數值無符號右移指定位數,並且結果進棧

       

      0x7E

      iand

      棧頂兩int型數值按位與,並且結果進棧

      0x7F

      land

      棧頂兩long型數值按位與,並且結果進棧

       

      0x80

      ior

      棧頂兩int型數值按位或,並且結果進棧

      0x81

      lor

      棧頂兩long型數值按位或,並且結果進棧

       

      0x82

      ixor

      棧頂兩int型數值按位異或,並且結果進棧

      0x83

      lxor

      棧頂兩long型數值按位異或,並且結果進棧

       

      0x84

      iinc

      指定int型變量增加指定值

       

      0x85

      i2l

      棧頂int值強轉long值,並且結果進棧

      0x86

      i2f

      棧頂int值強轉float值,並且結果進棧

      0x87

      i2d

      棧頂int值強轉double值,並且結果進棧

      0x88

      l2i

      棧頂long值強轉int值,並且結果進棧

      0x89

      l2f

      棧頂long值強轉float值,並且結果進棧

      0x8A

      l2d

      棧頂long值強轉double值,並且結果進棧

      0x8B

      f2i

      棧頂float值強轉int值,並且結果進棧

      0x8C

      f2l

      棧頂float值強轉long值,並且結果進棧

      0x8D

      f2d

      棧頂float值強轉double值,並且結果進棧

      0x8E

      d2i

      棧頂double值強轉int值,並且結果進棧

      0x8F

      d2l

      棧頂double值強轉long值,並且結果進棧

      0x90

      d2f

      棧頂double值強轉float值,並且結果進棧

      0x91

      i2b

      棧頂int值強轉byte值,並且結果進棧

      0x92

      i2c

      棧頂int值強轉char值,並且結果進棧

      0x93

      i2s

      棧頂int值強轉short值,並且結果進棧

       

      0x94

      lcmp

      比較棧頂兩long型數值大小,並且結果(1,0,-1)進棧

      0x95

      fcmpl

      比較棧頂兩float型數值大小,並且結果(1,0,-1)進棧;當其中一個數值為NaN時, -1進棧

      0x96

      fcmpg

      比較棧頂兩float型數值大小,並且結果(1,0,-1)進棧;當其中一個數值為NaN時,1進棧

      0x97

      dcmpl

      比較棧頂兩double型數值大小,並且結果(1,0,-1)進棧;當其中一個數值為NaN時,-1進棧

      0x98

      dcmpg

      比較棧頂兩double型數值大小,並且結果(1,0,-1)進棧;當其中一個數值為NaN時,1進棧

       

      0x99

      ifeq

      當棧頂int型數值等於0時跳轉

      0x9A

      ifne

      當棧頂int型數值不等於0時跳轉

      0x9B

      iflt

      當棧頂int型數值小於0時跳轉

      0x9C

      ifge

      當棧頂int型數值大於等於0時跳轉

      0x9D

      ifgt

      當棧頂int型數值大於0時跳轉

      0x9E

      ifle

      當棧頂int型數值小於等於0時跳轉

      0x9F

      if_icmpeq

      比較棧頂兩int型數值大小,當結果等於0時跳轉

      0xA0

      if_icmpne

      比較棧頂兩int型數值大小,當結果不等於0時跳轉

      0xA1

      if_icmplt

      比較棧頂兩int型數值大小,當結果小於0時跳轉

      0xA2

      if_icmpge

      比較棧頂兩int型數值大小,當結果大於等於0時跳轉

      0xA3

      if_icmpgt

      比較棧頂兩int型數值大小,當結果大於0時跳轉

      0xA4

      if_icmple

      比較棧頂兩int型數值大小,當結果小於等於0時跳轉

      0xA5

      if_acmpeq

      比較棧頂兩引用型數值,當結果相等時跳轉

      0xA6

      if_acmpne

      比較棧頂兩引用型數值,當結果不相等時跳轉

       

      0xA7

      goto

      無條件跳轉

       

      0xA8

      jsr

      跳轉至指定16位offset位置,並將jsr下一條指令地址壓入棧頂

      0xA9

      ret

      返回至局部變量指定的index的指令位置(通常與jsr、jsr_w聯合使用)

      0xAA

      tableswitch

      用於switch條件跳轉,case值連續(可變長度指令)

      0xAB

      lookupswitch

      用於switch條件跳轉,case值不連續(可變長度指令)

       

      0xAC

      ireturn

      當前方法返回int

      0xAD

      lreturn

      當前方法返回long

      0xAE

      freturn

      當前方法返回float

      0xAF

      dreturn

      當前方法返回double

      0xB0

      areturn

       

      指令格式:  areturn

       

      功能描述:  從方法中返回一個對象的引用。

       

      拋出異常:  如果當前方法是synchronized方法,

                 並且當前線程不是改方法的鎖的擁有者,

                 會拋出

                 IllegalMonitorStateException。

       

      指令執行前

      指令執行后

      棧底

      ...

       

      objectref

       

      棧頂

       

      objectref  :  被返回的對象引用。

       

      0xB1

      return

      當前方法返回void

       

      0xB2

      getstatic

      獲取指定類的靜態域,並將其值壓入棧頂

      0xB3

      putstatic

      為指定的類的靜態域賦值

      0xB4

      getfield

      獲取指定類的實例域,並將其值壓入棧頂

      0xB5

      putfield

      為指定的類的實例域賦值

       

      0xB6

      invokevirtual

      調用實例方法

      0xB7

      invokespecial

      調用超類構造方法、實例初始化方法、私有方法

      0xB8

      invokestatic

      調用靜態方法

      0xb9

      invokeinterface

      調用接口方法

       

      0xBA

      ---

      因為歷史原因,該碼點為未使用的保留碼點

       

      0xBB

      new

      創建一個對象,並且其引用進棧

      0xBC

      newarray

      創建一個基本類型數組,並且其引用進棧

      0xBD

      anewarray

       

      指令格式:  anewarray index1 index2

       

      功能描述:  棧頂數值(count)作為數組長度,創建

                 一個引用 型數組。棧頂數值出棧,數組引

                 用進棧。

       

      拋出異常:  如果count小於0,會拋出

                 NegativeArraySizeException

       

      指令執行前

      指令執行后

      棧底

      ...

      ...

      count

      arrayref

      棧頂

       

      count      :  int類型。

      arrayref   :  對所創建的數組的引用。

       

      0xBE

      arraylength

       

      指令格式:  arraylength

       

      功能描述:  棧頂的數組引用(arrayref)出棧,該

                 數組的長度進棧。

       

      拋出異常:  如果arrayref的值為null,會拋出

                 NullPointerException。

       

      指令執行前

      指令執行后

      棧底

      ...

      ...

      arrayref

      length

      棧頂

       

      arrayref   :  數組引用

      length     :  數組長度

       

       

      0xBF

      athrow

       

      指令格式:  athrow

       

      功能描述:  將棧頂的數值作為異常或錯誤拋出

       

      拋出異常:  如果棧頂數值為null,則使用

                 NullPointerException代替棧頂數

                 值拋出。

                 如果方法是的,則有可能拋出

                 IllegalMonitorStateException。

       

      指令執行前

      指令執行后

      棧底

      ...

      objectref

      objectref

       

      棧頂

       

      objectref  :  Throwable或其子類的實例的引用。

       

      0xC0

      checkcast

      類型轉換檢查,如果該檢查未通過將會拋出ClassCastException異常

      0xc1

      instanceof

      檢查對象是否是指定的類的實例。如果是,1進棧;否則,0進棧

       

      0xC2

      monitorenter

      獲得對象鎖

      0xC3

      monitorexit

      釋放對象鎖

       

      0xC4

      wide

      用於修改其他指令的行為

       

      0xC5

      multianewarray

      創建指定類型和維度的多維數組(執行該指令時,棧中必須包含各維度的長度值),並且其引用值進棧

       

      0xC6

      ifnull

      為null時跳轉

      0xC7

      ifnonnull

      不為null時跳轉

      0xC8

      goto_w

      無條件跳轉(寬索引)

      0xC9

      jsr_w

      跳轉至指定32位offset位置,並且jsr_w下一條指令地址進棧

       

      0xCA

      breakpoint

       

       

      0xFE

      impdep1

       

      0xFF

      impdep2

       

      來源於:http://www.oseye.net/user/kevin/blog/304


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM