Java之字節碼(1) - 深入解析


轉載地址

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

  類文件{

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

  }

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

  例子:有一個Java類Demo.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文件的字節碼信息

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

Demo.txt:

 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 2: 0 
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 17: 0 
79    line 18: 8 
80   LocalVariableTable: 
81    Start  Length  Slot  Name   Signature 
82    0      9      0    this       LDemo; 
83 }

解析:

  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

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 后字節碼信息:

Compiled from "TestString.java" 
public class TestString extends java.lang.Object{ 
public TestString(); 
  Code: 
   0:      aload_0 
   1:      invokespecial  #8; //Method java/lang/Object."<init>":()V 
   4:      return 
  
public java.lang.String testString(java.lang.String, java.lang.String); 
  Code: 
   0:      new #16; //class java/lang/StringBuilder 
   3:      dup 
   4:      aload_1 
   5:      invokestatic    #18; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; 
   8:      invokespecial  #24; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 
   11:     aload_2 
   12:    invokevirtual  #27; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
   15:    invokevirtual  #31; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
   18:    areturn 
  
public java.lang.String testStringBuffer(java.lang.StringBuffer, java.lang.String); 
  Code: 
   0:      aload_1 
   1:      aload_2 
   2:      invokevirtual  #40; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 
   5:      invokevirtual  #45; //Method java/lang/StringBuffer.toString:()Ljava/lang/String; 
   8:      areturn 
}

 

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

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

 
 


免責聲明!

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



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