總網頁瀏覽量

2016年3月10日 星期四

[java] Constant pool

  JVM在每種類型(type)加載時, 都提供(store)一個常量池(constant pool)記錄。常量池是一個有序(ordered)的集合,集合內存放類型所使用的常量,包含string、integer、floating point constants和 symbolic references to types、fields、and methods。

  常數池通過索引參照。因為常量池儲存了宣告(symbolic references)引用之型態(type),參數(field)、方法所使用到之型態(methods used by a type),動態鍊結的方式在java program裡作控制.[2]

  一般而言,constant pool都包含魔術數字(Magic Number,[3])和版本編號(version numbers),像是在JVM介紹裡, constant pool包含檔案中常數(constant)相關(associate)的類別或介面,這些常數就像文字字串(literal string)般顯示,將final variable、class names、method name存在池中。常量池一個有組織entries列表,在list這些entries列表的數量,constant_cool.count,而列表(list)的,precedes the actual list。


  constant_pool常量池中許多entries都會指到(refer to)其他entries,許多條目(items)也是一樣,在整個class file檔案中,常量池都有一個編號給與對應到位於哪個constant_pool的位置,在list中的第一項索引為1,以此類推。雖然constant_pool列表中沒有索引為0的入口,
但是仍然會進算於constant_pool_count裡面。
假如 constant_pool含有十四項entries(#1-#14),在constant_pool_count則還是15。 (?)

  constant pool 進去後, 就會用 one-byte tag作為開頭, 這個tag包含constant 的類別(type),
一旦JVM開始執行翻譯這tag, 就知道此tag後的常量類型是什麼


able 6-3. Constant pool tags
Entry Type           Tag Value    Description
CONSTANT_Utf8          1     A UTF-8 encoded Unicode string
CONSTANT_Integer      3     An int literal value
CONSTANT_Float         4     A float literal value
CONSTANT_Long         5     A long literal value
CONSTANT_Double      6     A double literal value
CONSTANT_Class        7     A symbolic reference to a class or interface
CONSTANT_String        8     A String literal value
CONSTANT_Fieldref   9     A symbolic reference to a field
CONSTANT_Methodref    10     A symbolic reference to a method declared in a class
CONSTANT_InterfaceMethodref     11     A symbolic reference to a method declared in an                    interface
CONSTANT_NameAndTyp  12     Part of a symbolic reference to a field or method

舉例來講 
#11=Utf8  main

#11 代表索引值,UTF8 是tag 代表,接在後方的constant其類別。

每個entry type都和一個tag value對應,這些tag的名字都會在後面加上

The constant pool plays an important role in the dynamic linking of Java programs.(簡單免翻)
除了 literal constant values(字面常量,值接量),也包含三中符號引用:
1. fully qualified names of classes and interfaces
2. field names and descriptors
3. method names and descriptors

A field is an instance or class variable of the class or interface.
A field descriptor is a string that indicates the fields type. 
A method descriptor is a string that indicates the methods return type and the number, order, and types of its parameters.

The constant pools fully qualified names and method and field descriptors are used at run time to link code in this class or interface with code and data in other classes and interfaces.


class file 不包含內部的 memory layout原件,就是記憶體分配布局之信息,所以classes、fieldes、method不能值接參考到class file 中的bytecode。

JVM在run time時,從constant pool中給予其對映的參考(reference),就值接解決要使用address 的參照。比如果,在bytecode的指令碼索引參考到常量池之索引給予JVM執行。


- - - - -
Constant pool是個有順序性的cp_info列表,cp_info列表中的tag,是個unsigned byte,
表示的variety和format。其中,cp_info有十一個值,每個都描述。

Type  Name  Count
  u1          tag               1
  u1          info            depends on tag valuse

HellowWorld.java

public class HellowWorld{
  public static void main(String args[]){
    System.out.println("HAAAAA"); 
  }
}

 
Constant pool:
 #1 = Methodref     #6.#15         // java/lang/Object."<init>":()V
 #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
 #3 = String             #18            // HAAAAA
 #4 = Methodref    #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V
 #5 = Class              #21            // HellowWorld
 #6 = Class              #22            // java/lang/Object
 #7 = Utf8               <init>
 #8 = Utf8               ()V
 #9 = Utf8               Code
#10 = Utf8               LineNumberTable
#11 = Utf8               main
#12 = Utf8               ([Ljava/lang/String;)V
#13 = Utf8               SourceFile
#14 = Utf8               HellowWorld.java
#15 = NameAndType        #7:#8          // "<init>":()V
#16 = Class              #23            // java/lang/System
#17 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
#18 = Utf8                HAAAAA
#19 = Class              #26            // java/io/PrintStream
#20 = NameAndType        #27:#28        // println:(Ljava/lang/String;)V
#21 = Utf8               HellowWorld
#22 = Utf8               java/lang/Object
#23 = Utf8               java/lang/System
#24 = Utf8               out
#25 = Utf8               Ljava/io/PrintStream;
#26 = Utf8               java/io/PrintStream
#27 = Utf8               println
#28 = Utf8               (Ljava/lang/String;)V


 
The resulting bytecode 
public HellowWorld();
  descriptor: ()V
  flags: ACC_PUBLIC
  Code:
    stack=1, locals=1, args_size=1
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 1: 0
public static void main(java.lang.String[]);
  descriptor: ([Ljava/lang/String;)V
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
    stack=2, locals=1, args_size=1
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String  HAAAAA
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
    LineNumberTable:
      line 3: 0
      line 4: 8



由這個簡單的程式中理解bytecode.[1]

有兩個method在bytecode裡面, 一個是HellowWorld, 另一個是 main,
但是哪個是程式第一個執行的method? 
我們在 HellowWorld.java只定義了一個method, 
不是只有main怎麼現在bytecode中又有兩個method呢?
程式的第一個method, 是HellowWorld, 
這個method預設建構子(default constructors)出來是由程式編譯時編譯器補上的, 
如你所期待的那樣, 這個預設建構子是public且沒有參數(arguments)的.
所以, 他們的產生是由編譯器(Compiler)執行並直接放置(put)在bytecode的.

現在看到bytecode部分, 觀察一下method內的地方.
開始的預設建構子在bytecode這裡, 開始的第一行:

0: aload_0

開始值為 0(0:) 這個是一開始執行method所表示的偏移量(offset).
表示這個為首的指令instruction標記他是 0, 這就是為什麼要在這裡討論他,
直接看這個值非常不合邏輯. 而剩下的其他指令是operator稱為opcode.
 - - - - - 
應該是這個吧( ?!

Code: 
  0: aload_0 
  1: invokespecial #1; //Method java/lang/Object."":()V 
  4: return
- - - - -

從第一行中, 我們將會把 local variable table的值放(push) 到stack.
在我們的這個例子裡, 我們確實只會放了一個參考(reference)就this, 
但他是不存在的指令instrucation.

所以我們希望下一行指令做啥??

  1: invokespecial #1; //Method java/lang/Object."":()V 

為什麼我們要知道建構子?
為什麼建構子是第一件做的事?
他執行(invokes)父類別(parent class) 建構子.

這只是, 這行不只做了這件事情阿-他執行了父類別的建構子(這個例子中,物件). 
那為什麼是跟 #1 ? 這就是一個 constant table 的 index.
index #1 參考到 建構子的物件.

最後, 最下面那行, 回傳我們的建構子.

這雖然不是很屌的method, 但是確可以幫助我們一路了解到從頭到尾的bytecode.
make head or tail of bytecode 這英文好玩.

0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;

這行的opcode getstatic. 
你可能就會猜, 這個opcode拿到一個static field(在這個例子中, System.out)
和push他到 operand stack中. 當你可能又在猜, #2 就牽涉到field到我們的constant table中.


3: ldc #3; //String HAAAAA
 

這行的ldc,載入(load)這個參數到operand stack中, 從這點來看我們載入的不管是不是

哪一種參數都會在constant table的編號 #3 中 . 這行就是我們的String, "HAAAAA"

 

接下來,

 
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V

 
這行執行了我們物件System.out中的println的method, 這個過程的執行是做pop兩個
operands移出stack和執行method, 之後我們的method就會結束, 然後return.

 
 
 
[1] http://www.javaranch.com/journal/200408/Journal200408.jsp#a1 
[2] http://denverj.iteye.com/blog/1210979 
[3] http://openhome.cc/Gossip/JavaEssence/SourceTarget.html

沒有留言:

張貼留言