Java 字節碼之解析一

轉   http://15838341661-139-com.iteye.com/blog/1287866


一: Java 字節代碼的組織形式

 

類文件 {

    OxCAFEBABE ,小版本號,大版本號,常量池大小,常量池數組,訪問控制標記,當前類信息,父類信息,實現的接口個數,實現的接口信息數組,域個數,域信息數組,方法個數,方法信息數組,屬性個數,屬性信息數組

}

 

二: 查看方法 --- javap 命令

 

例子:有一個 Java 類 Demo.java


Java代碼  收藏代碼
  1. public class Demo {  
  2.     private String str1;  
  3.     private String str2;  
  4.     private int num1;  
  5.     private int num2;  
  6.     public static final String STATIC_DATA = "hello world";  
  7.       
  8.     private void sayHello1(){  
  9.         System.out.println("this is method1...");  
  10.     }  
  11.     private void sayHello2(){  
  12.         System.out.println("this is method2...");  
  13.     }  
  14.     public void sayHello3(){  
  15.         System.out.println("this is method3...");  
  16.     }  
  17. }  
 

 

通過 jdk 自帶的反編譯工具命令 javap 可以查看 class 文件的字節碼信息


D:\>javap -verbose Demo >> Demo.txt


   Demo.txt :

 

Aaa代碼  收藏代碼
  1. Compiled from "Demo.java"  
  2. public class Demo extends java.lang.Object  
  3.   SourceFile: "Demo.java"  
  4.   minor version: 0  
  5.   major version: 49    
  6.    
  7.   Constant pool:  
  8. const #1 = class      #2;   //  Demo  
  9. const #2 = Asciz     Demo;  
  10. const #3 = class      #4;   //  java/lang/Object  
  11. const #4 = Asciz     java/lang/Object;  
  12. const #5 = Asciz     str1;  
  13. const #6 = Asciz     Ljava/lang/String;;  
  14. const #7 = Asciz     str2;  
  15. const #8 = Asciz     num1;  
  16. const #9 = Asciz     I;  
  17. const #10 = Asciz   num2;  
  18. const #11 = Asciz   STATIC_DATA;  
  19. const #12 = Asciz   ConstantValue;  
  20. const #13 = String  #14; //  hello world  
  21. const #14 = Asciz   hello world;  
  22. const #15 = Asciz   <init>;  
  23. const #16 = Asciz   ()V;  
  24. const #17 = Asciz   Code;  
  25. const #18 = Method       #3.#19;   //  java/lang/Object."<init>":()V  
  26. const #19 = NameAndType    #15:#16;//  "<init>":()V  
  27. const #20 = Asciz   LineNumberTable;  
  28. const #21 = Asciz   LocalVariableTable;  
  29. const #22 = Asciz   this;  
  30. const #23 = Asciz   LDemo;;  
  31. const #24 = Asciz   sayHello1;  
  32. const #25 = Field   #26.#28;  //  java/lang/System.out:Ljava/io/PrintStream;  
  33. const #26 = class    #27; //  java/lang/System  
  34. const #27 = Asciz   java/lang/System;  
  35. const #28 = NameAndType    #29:#30;//  out:Ljava/io/PrintStream;  
  36. const #29 = Asciz   out;  
  37. const #30 = Asciz   Ljava/io/PrintStream;;  
  38. const #31 = String  #32; //  this is method1...  
  39. const #32 = Asciz   this is method1...;  
  40. const #33 = Method       #34.#36;  //  java/io/PrintStream.println:(Ljava/lang/String;)V  
  41. const #34 = class    #35; //  java/io/PrintStream  
  42. const #35 = Asciz   java/io/PrintStream;  
  43. const #36 = NameAndType    #37:#38;//  println:(Ljava/lang/String;)V  
  44. const #37 = Asciz   println;  
  45. const #38 = Asciz   (Ljava/lang/String;)V;  
  46. const #39 = Asciz   sayHello2;  
  47. const #40 = String  #41; //  this is method2...  
  48. const #41 = Asciz   this is method2...;  
  49. const #42 = Asciz   sayHello3;  
  50. const #43 = String  #44; //  this is method3...  
  51. const #44 = Asciz   this is method3...;  
  52. const #45 = Asciz   SourceFile;  
  53. const #46 = Asciz   Demo.java;  
  54.    
  55. {  
  56. public static final java.lang.String STATIC_DATA;  
  57.   Constant value: String hello world  
  58. public Demo();  
  59.   Code:  
  60.    Stack=1, Locals=1, Args_size=1  
  61.    0:      aload_0  
  62.    1:      invokespecial  #18; //Method java/lang/Object."<init>":()V  
  63.    4:      return  
  64.   LineNumberTable:  
  65.    line 20  
  66.   LocalVariableTable:  
  67.    Start  Length  Slot  Name   Signature  
  68.    0      5      0    this       LDemo;  
  69.    
  70. public void sayHello3();  
  71.   Code:  
  72.    Stack=2, Locals=1, Args_size=1  
  73.    0:      getstatic   #25; //Field java/lang/System.out:Ljava/io/PrintStream;  
  74.    3:      ldc   #43; //String this is method3...  
  75.    5:      invokevirtual  #33; //Method java/io/PrintStream.println:(Ljava/lang/String;)V  
  76.    8:      return  
  77.   LineNumberTable:  
  78.    line 170  
  79.    line 188  
  80.   LocalVariableTable:  
  81.    Start  Length  Slot  Name   Signature  
  82.    0      9      0    this       LDemo;  
  83.    
  84. }  
 

 

解析:

1. 版本號   major version: 49   //java 版本 jdk1.6 顯示的是 50 , jdk1.5 顯示的是 49 , jdk1.4 顯示的是 58 , 高版本能執行低版本的 class 文件  

2. 常量池 Constant pool

Method :方法

Field :字段

String :字符串

Asciz :簽名如<init> 由jvm 調用,其他是不能夠去調用它的

NameAndType :變量名的類型

Class :類

 

通過字節碼,我們可以看到 Demo 類 繼承於 java.lang.Object ,如果類中沒有顯式聲明構造函數的話,編譯器會插入一個缺省無參的構造函數 ( 構造函數在 JVM 級別是顯示成 <init> 的普通函數 ) 。

 

 

三: 檢測代碼的效率問題

 

學習 Java 的過程中,都會瞭解到字符串合併時要用到 StringBuffer 來代替 String ,那下面就來通過 Java 字節碼來驗證兩種方式的效率性。

 

例子:一個 Java 類 TestString.java

Java代碼  收藏代碼
  1. <strong>public class TestString {  
  2.     public String testString(String str1, String str2){  
  3.        return str1 + str2;  
  4.     }  
  5.     public String testStringBuffer(StringBuffer sb, String str){  
  6.        return sb.append(str).toString();  
  7.     }  
  8. }  
  9.  </strong>  

 

 

javap –c TestString 後字節碼信息:

 

 

Java代碼  收藏代碼
  1. Compiled from "TestString.java"  
  2. public class TestString extends java.lang.Object{  
  3. public TestString();  
  4.   Code:  
  5.    0:      aload_0  
  6.    1:      invokespecial  #8//Method java/lang/Object."<init>":()V  
  7.    4:      return  
  8.    
  9. public java.lang.String testString(java.lang.String, java.lang.String);  
  10.   Code:  
  11.    0:      new #16//class java/lang/StringBuilder  
  12.    3:      dup  
  13.    4:      aload_1  
  14.    5:      invokestatic    #18//Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;  
  15.    8:      invokespecial  #24//Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V  
  16.    11:     aload_2  
  17.    12:    invokevirtual  #27//Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;  
  18.    15:    invokevirtual  #31//Method java/lang/StringBuilder.toString:()Ljava/lang/String;  
  19.    18:    areturn  
  20.    
  21. public java.lang.String testStringBuffer(java.lang.StringBuffer, java.lang.String);  
  22.   Code:  
  23.    0:      aload_1  
  24.    1:      aload_2  
  25.    2:      invokevirtual  #40//Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;  
  26.    5:      invokevirtual  #45//Method java/lang/StringBuffer.toString:()Ljava/lang/String;  
  27.    8:      areturn  
  28.    
  29. }  
  30.    
 

從上面編譯後的字節碼信息可以看出來,方法testString 調用了五個方法:new 、invokestatic 、invokespecial 和兩個invokevirtual ; 而testStringBuffer 方法只調用了兩個invokevirtual 方法。第一個方法比第二個方法多做了好多工作,其效率當然是要低的。而且我們從java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

可以看出來其實對於String 字符串合併,內部還是轉化爲StringBuilder 的方法調用,這是因爲String 是長度不可變的,所以不如直接採用StringBuilder (與StringBuffer 長度都是可變的,只不過前者是非線程安全,後者是線程安全)進行字符串合併。
 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章