類和類的裝載 我們來看一下類以及它們被JVM裝載的時候做了些什麼?
在這個新的有關動態的Java編程特征的系列文章中
將會看到在正在執行的Java應用程序的背後發生了些什麼
企業級Java專家Dennis Sosnoski給出了Java二進制格式和發生在JVM內部的類中的事情
遵循這條路線
他介紹正在裝載的類所影響的范圍(從正在運行的一簡單的Java應用程序所必須的大量的類到在J
EE和類似的復雜的框架結構中類裝載器沖突所可能導致的問題)
這篇文章揭示了Java動態編程這組主題所包含的一系列的新的知識
這些主題包括從Java二進制類文件格式的結構到使用反射訪問運行時的元數據
以及所有的在運行時編輯和構造新的類的方法
貫穿這個材料的全部基本路線是Java平台的編程思想
是比用其它直接編譯成本地代碼的語言更加動態的工作
如果你理解了這些動態的特征
你就可用Java語言做一些用其它的主流的編程語言所不能做的事情
在這篇文章中
我介紹了位於Java平台的動態特征之下的一些基本概念
這些概念圍繞用於描述Java類的二進制格式
包括類被裝載進JVM(Java虛擬機)時所發生的事情
這篇文件不僅為理解這個系列主題的其它文章提供基礎
同時也演示了一些非常實際的在Java平台上工作的開發人員所關心的事情
一個類的二進制形式 用Java語言的開發人員通常不必關心通過編譯器運行他們的源代碼時所發生的一些細節問題
在這個系列主題中
我會介紹許多有關從源代碼到可執行的程序這個過程的背後細節
因此
我們先來看一下編譯器所產生的二進制類
二進制類的格式實際上是被JVM(Java虛擬機)規范定義的
正常的類的描述是一個編譯器利用Java語言的源代碼生成的
並且通常被保存在一以
class為擴展名的文件中
但是這些特征都不是本質的
其它的一些編程語言已經被開發使用Java的二進制類的格式
並且
因為一些目的
新的類的描述被創建並且被直接裝載進一個正在執行的JVM中
但是JVM所關心的
重要的不是這些源代碼或它是怎樣被存儲的
而是這個格式自身
因此
先來這種類格式看上去象什麼呢?下面(List
)列出了一個非常短的類的源代碼
緊跟著是用編譯器輸出的這個類文件的一部分十六進制的顯示
List
Hello
java的源代碼和(部分)二進制表示
public class Hello
{
public static void main(String[] args) {
System
out
println(
Hello
World!
);
}
}
: cafe babe
e
a
a
c
:
d
e
f
a
:
c
e
e
<init>
()
:
f
d
e
V
Code
main
:
b
c
a
f
c
e
f
([Ljava/lang/S
:
e
b
c
tring;)V
:
c
d
c
c
f
c
Hello
W
:
f
c
c
orld!
:
c
c
f
a
f
c
e Hello
java/lan
:
f
f
a
a
f g/Object
java/
a
:
c
e
f
d
f
lang/System
ou
二進制的內部 List
中所顯示的二進制類的表示的第一件事情是標識Java二進制類的格式的
café babe
簽名
這個簽名只是一種確認實際請求的Java類的格式的一個實例的數據塊的簡易方法
每個Java的二進制類
即使在不同的文件系統上
也需要用這四個字節開始
數據的其它部分不是很有趣
跟在簽名後面是一對類格式的版本號(在這個例子中
用
javac編譯生成的時候
會產生次版本為
主版本為
十六進制的形式是
x
e的版本號)
然後是常量池中的條目的計數
跟在條目計數(在這個例子中是
或
x
a)後面的是實際的常量池數據
這是保存所有類定義所使用的常量的地方
它包括類和方法的名字
簽名以及字符串(這些字符串是你能夠認可的在十六制的存放處的正確性的文本解釋)
以及連同在一起的各種二進制值
在常量池中項目是可變長度的
每個項目的第一個字節標識了項目的類型和它應該怎樣被解碼
我不打算對這些內容做詳細介紹
如果你有興趣以實際的JVM規范開始
這裡有許多有用參考
關鍵點是常量池包含了所有的對其它類和這個類所使用的方法的引用
還有這個類自身以及它的方法的實際定義
盡管平均值可能會少一些
但是常量池的大小很容易的超過二進制類的在小的一半或更多
跟在常量池後面是幾個引用常量池條目的項目
它們是類本身
它的超類以及接口
這些項目的後面是有關字段和方法的信息
這些信息是做為復合結構來描述自己的
對於方法的可執行代碼以代碼屬性(code attributes)的形式被包含在方法的定義中
這種代碼是JVM的指令形式
通常叫做字節碼(bytecode)
這是下一節的主題之一
在Java類的格式中屬性(Attributes)用來做為幾種定義的用途
包括已經提到的字節碼(bytecode)
用於字段的常量值
異常處理
以及調試信息
但是
屬性(Attributes)不只有這些可能的用途
從一開始
JVM規范要求JVMs(Java虛擬機)忽略未知類型的屬性
這種要求對於屬性的使用提供了靈活性
使得它在將來能夠服務於其它的用途
例如提供與用戶類一起工作的框架所需要的元信息
這是一種Java源於C#語言所廣泛使用的方法
不幸的是
no hook have yet been provided for making of this flexibility at the user level
字節碼和堆棧 組成類文件的可執行部分的字節碼是適應特定類型計算機(JVM)是的實際的機器碼
這所以叫做虛擬機是因為它是用軟件來設計實現的
而不是硬件
每個運行在JVM上的應用程序都是建立在這種機器的一種實現
虛擬機實際上相當的簡單
它使用堆棧結構
這就意味著它們在被使用之前指令操作要被裝載進一個內部的堆棧
指令集包括所有的一般的算術運算和邏輯操作
還有有條件和無條轉移
裝載/存儲
調用/返回
堆棧的維護
以及幾種特殊的指令類型
包括立即數的一些指令被直接編碼進指令
另外一些直接從常量池引用值
雖然虛擬機是簡單的
但執行起來卻不是這樣的
第一代JVM基本上是虛擬機的字節碼的解析器
相對而言
比較簡單
但卻遇到嚴重的性能問題———解析代碼總是要比執行本地代碼花費更長的時間
為了減少這些性能問題
第二代JVM添加了即時(JIT)翻譯
JIT技術是在Java字節碼第一次執行之前把它編譯成本地代碼
從而為重復執行提供了更好的性能
當前的JVM做的更好
它使用相應的技術來監控程序的執行並且選擇性使使用代碼得到優化
裝載類 把源代碼編譯成本地代碼的語言(如C和C++)在源代碼被編譯之後通常需要鏈接這樣的步驟
這種鏈接過程把獨立編譯的源文件連同共享類庫的代碼合並到一起
從而形成一個可執行的程序
Java語言是不同的
使用Java語言
編譯器生成的類文件一般情況下單獨保存的
直到它們裝載進一個JVM為止
即使是建立一個JAR文件也不會改變這種情況———JAR文件只是類文件的一個容器
優於一個分開的步驟
JVM把類裝載進內存的時候
鏈接類成為JVM所要執行的工作的一部分
這樣就可以在初始化裝載的時候增加一些系統開銷
但是也為Java應用程序提供了高級的靈活性
例如
應用程序可以使用直到運行時才知道的實際實現的接口來編寫
這種後期綁定(late binding)的方法來裝配一個應用程序在Java平台中被廣泛使用
servlets就是一個普通的例子
對於裝載類的規則在JVM規范的細節中被清楚的說明了
基本原則是類只有在需要的時候才被裝載(或者至少是顯示的裝載
JVM的這種方法在實際裝載過程中有一些靈活性
但是必需保持一個固定的類初始化的順序)
每個被裝載的類可以有其它的它所依賴的類
因此裝載過程是遞歸的
在Listing
中的類顯示了這種遞歸裝載是怎樣工作的
這個Demo類包含了一個簡單的創建Greeter類的一個實例並且調用這個類的greet方法的main方法
Greeter類的構造器創建了一個Message的實例
然後它在greet方法中使用這個Message實例
Listing
用於類裝載演示的源碼
public class Demo
{
public static void main(String[] args) {
System
out
println(
**beginning execution**
);
Greeter greeter = new Greeter();
System
out
println(
**created Greeter**
);
greeter
greet();
}
}
public class Greeter
{
private static Message s_message = new Message(
Hello
World!
);
public void greet() {
s_message
print(System
out);
}
}
public class Message
{
private String m_text;
public Message(String text) {
m_text = text;
}
public void print(java
io
PrintStream ps) {
ps
println(m_text);
}
}
設置java命令的命令行參數為
verbose:class
這樣就可打印類裝載過程的軌跡
Listing
顯示了使用這個參數的來運行Listing
時的部分輸出
[Opened /usr/java/j
sdk
/jre/lib/rt
jar]
[Opened /us
From:http://tw.wingwit.com/Article/program/Java/hx/201311/27138.html