JVM 學習筆記
Jack.Wang(本文未完,待續…..)
摘要:JVM 作為 Java 的核心技術,很多朋友想必也有研究。一直都在關注 JVM 方面的技術,以前看過一些書籍和網上的資料,自己也發了些 Blog 文章,不過還是沒有徹底的了解 JVM 機制,最近有時間研究了研究,特此寫下一篇文章并結合筆者多年實踐以揭露 JVM 實現機理,本文后面提供資料下載,讀者可進一步研究。
關鍵字: JVM,Java 技術,虛擬機,Java 架構
目錄
1 JVM架構引言
2 JVM安全框架
3 JVM內部機理
3.1 JVM的生命周期
3.2 JVM的框架
3.3 數據類型
3.3.1 Java數據類型
3.3.2 浮點運算
3.4 方法區
3.5 操作數棧
3.6 本地方法棧
3.7 執行引擎
4 類文件結構
5 線程同步
6 垃圾回收機制
7 總結
8 參考資料
文中的圖片沒有上傳,不過想看圖片的可以下載原文。
本文下載地址 JVM學習筆記.doc
1 JVM架構引言
Java 的平臺獨立,安全和網絡可移植性是的 Java 最適合網絡計算環境,java 的四大核心技術是,Java 語言,字節碼格式,Java API,JVM。每次你編寫Java程序時你就用到了這四項技術,那么你是否對他們有足夠的了解呢?
很多人把 JVM 看成是 Java 的解釋器,這是錯誤的理解,并不是所有的 JVM 實現都 Java 的解釋器,有的也用到了 JIT (Just-In-Time) 技術。Java 是不允許直接操作本地機器指令的,對于Java方法來說有兩種:Java和native,對于native我更習慣用C++ 寫DLL,但Java并不提倡你這么做,因為有損Java平臺獨立性。JVM 中除了執行引擎就是類加載器了,ClassLoader也分為兩種:原始加載器和加載器Object,原始加載器使用和寫JVM 一樣的語言寫的,比如用C寫的類加載器,而加載器Object就是用 Java 實現的類加載器,方便我們擴展,比如你自己可以 New 一個 URLClassLoader 從網絡上下載字節碼到本地運行。一個類的加載和他參考的類的加載應該用同一個 ClassLoader。這一點在發生異常的時候很難找出,比如 OSGI 中每個 bundle 都有自己獨立的 ClassLoader,對于新手很容易犯錯誤而無從下手,我們熟悉的WEB 服務器 Tomcat 的類加載器是分層(有繼承關系)的,所以在應用整合的時候也很容易發生 ClassLoader 相關的異常,而這樣的異常往往很難定位。平臺互異的字節序問題,在Java中,字節碼是大字節序的。Java 為支持開發者開發應用軟件提供了大量的 API,可以說,在計算機領域的大部分計算中Java都有對應的解決方案。
C++中可能比較受關注和困擾的就是指針了,而在Java中用“參考”這樣一個類似的東西代替了,參考不向指針那樣允許參與計算,避免了開發人員直接操作內存,還有個垃圾回收機制也避免了開發者手動釋放內存,還有就是 C++ 中的數組是不進行邊界檢查的而Java中每次使用數組的時候都要進行邊界檢查,豈不安全。 可見Java相比C++ 提高了開發效率和安全性。Java和C++ 比運行速度是個大問題,因此任何語言都不萬能的,在開發是我們應該適當權衡,Java運行速度低的原因主要有:
Ø Interpreting bytecodes is 10 to 30 times slower than native execution.
Ø Just-in-time compiling bytecodes can be 7 to 10 times faster than interpreting, but still not quite as fast as native execution.
Ø Java programs are dynamically linked.
Ø The Java Virtual Machine may have to wait for class files to download across a network.
Ø Array bounds are checked on each array access.
Ø All objects are created on the heap (no objects are created on the stack).
Ø All uses of object references are checked at run-time for null.
Ø All reference casts are checked at run-time for type safety.
Ø The garbage collector is likely less efficient (though often more effective) at managing the heap than you could be if you managed it directly as in C++.
Ø Primitive types in Java are the same on every platform, rather than adjusting to the most efficient size on each platform as in C++.
Ø Strings in Java are always UNICODE. When you really need to manipulate just an ASCII string, a Java program will be slightly less efficient than an equivalent C++ program.
2 JVM安全框架
Java 允許基于網絡的代碼運行和傳播,為其帶了安全問題。但Java也提供了內嵌的安全模型,開發者可高枕無憂。Java的沙箱安全模型使即時你不信任的代碼也可讓他在本機執行,如果是惡意的代碼他的惡意行為也會被沙箱攔截,所以在運行任何你有點懷疑的代碼前請確保你的沙箱沒有缺陷。
對于沙箱的四大基礎組件是:類加載器,類文件驗證,JVM安全特性,安全管理的API,其中最重要的是類加載器和安全管理API,因為他們可以客制化。對于加載器,每個 JVM 都可以有多個,同一個類可以加載多次到不同的 ClassLoader 中,類跨ClassLoader是不可見的,而在同一ClassLoader中是可直接訪問的,這樣可以隔離一些不安全的類。
類型檢查是很必要的,分為兩個階段,第一是在類加載進來的時候要進行類的合法性和完整性檢查,第二是運行時確認該類所參考的類,方法和屬性是否存在。類文件頭都是以一個四個字節的幻數開頭(0xCAFEBABE)來標識是個類文件,當然也有文件大小域,第一階段確保加載進來的類是正確格式,內部一直,Java語法語義限制一直,包括安全的可執行代碼,在這個過程中如果有錯誤,JVM會拋出異常,該類就不會被使用。第二階段其實由于動態連接的原因,需要在運行時檢查參考,因為 ClassLoader 在需要某些類時才去加載,延遲加載,在 ORM 產品中,比如 Hibernate, jdo 等都有所謂的延遲加載
SecurityManager 有一系列的 checkXXX 的方法,用來檢測相關操作是否合法,一般我們的程序是不用 SecurityManager 的,除非你安裝一個SecurityManager,如果沒有寫自己的策略文件,一般是用jre 下面的默認策略文件的設置,當然也可在 VM 運行參數設置策略文件的位置。SecurityManager 類的相關方法。
publicstatic SecurityManager getSecurityManager() {
return security;
}
publicstatic
void setSecurityManager(final SecurityManager s) {
try {
s.checkPackageAccess("java.lang");
} catch (Exception e) {
// no-op
}
setSecurityManager0(s);
}
privatestaticsynchronized
void setSecurityManager0(final SecurityManager s) {
SecurityManager sm = getSecurityManager();
if (sm != null) {
// ask the currently installed security manager if we
// can replace it.
sm.checkPermission(new RuntimePermission
("setSecurityManager"));
}
if ((s != null) && (s.getClass().getClassLoader() != null)) {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
s.getClass().getProtectionDomain().implies
(SecurityConstants.ALL_PERMISSION);
returnnull;
}
});
}
security = s;
InetAddressCachePolicy.setIfNotSet(InetAddressCachePolicy.FOREVER);}
設置自己的 SercurityManager:
System.setSecurityManager(new JackSecurityManager());
SecurityManager sm = System.getSecurityManager();
3 JVM內部機理
3.1 JVM的生命周期
任何一個類的 main 函數運行都會創建一個JVM實例,當main 函數結束JVM實例也就結束了,如果三個類分別運行各自的 main 函數則會創建3個不同的JVM實例。JVM實例啟動時默認啟動幾個守護線程,而 main 方法的執行是在一個單獨的非守護線程中執行的。只要母線程結束,子線程就自動銷毀,只要非守護main 線程結束JVM實例就銷毀了。
3.2 JVM的框架
JVM主要由 ClassLoader 子系統和執行引擎子系統組成,運行數據區分為五個部分,他們是方法區,堆,棧,指令寄存器,本地方法棧。方法區和堆是所有線程共享的,一般我們習慣對臨時變量放在寄存器中,但JVM中不用寄存器而是用棧,每個線程都有自己獨立的棧空間和指令計數器,其中棧里的元素叫幀,幀有三部分組成分別是局部變量,操作數棧和幀數據。正式因為棧是每個線程獨有的,所以對于 local 變量,是沒有多線程沖突問題的。棧和幀的數據結構和大小規范里沒有硬性規定,由實現者具體實做。
3.3 數據類型
3.3.1 Java數據類型
虛擬機的數據類型分為:原始類型和參考類型,有人一直說Java只有90%是OO的,其中原始類型就算的上,不過還好有對應的封裝類型如 int->Integer,JDK1.5可以用這些封裝類型直接做算術運算,不過Java內部要做拆箱和裝箱的工作。似乎 OO了些,不過現代的程序員才不過你這些,能用就行。
字長是數據值得基本單元,一般像 int, byte, short 等類型的值用一個字長存儲,而浮現類型用兩個字長存儲,字長的大小一般和機器的指針大小相同,最小是32 位。
像byte, short, or char 這樣的類型在方法區和堆中Java是原來類型,但在程序運行時放在棧幀中,統一用整型(int)來表示
對于類型之間的轉換JVM有對應的指令如:
上圖是小范圍向大范圍轉變,叫做向上轉型,當然也有向下轉型的指令,不過可能會造成數據被破壞,如圖:
不管你怎么變化,棧幀里都是以字為單位的,32位機子一個字是4個字節,正好是一個整型大小,如前所述原始類型在棧中都被表示為 int 型,當然如果是 long 和 double 那就用兩個字來表示咯。
3.3.2 浮點運算
浮點運算算法因該是倍受關注的了,Java中浮點數的表示遵照IEEE 754 1985的規范,這個規范定義了32位和64位浮點格式和運算,我都知道Java中的 float 是32位而,double是64位。對于浮點數大家都很清楚他分為四個部分:符號,尾數,基數和冪,要得到浮點數值必須將這四個部分相乘才能得到值。所有的浮點數都要經過標準化,我們都知道計算機里只有0,1這樣的表示,尾數和冪都要二進制表示,對于尾數float 用它的23位表示,double 用它的52位來表示,對于冪數 float 用它的 8位表示,double用它的11為表示,剩下以為表示符號。
JVM也有對應的浮點運算指令如圖(加法指令)
3.4 方法區
ClassLoader負責把class加載進來并解析類信息放在方法區,方法區是被所有線程共享的,所以方法區必須保證線程的安全。比如兩個線程同時需要一個類,必須保證只有一個線程去加載而另一個線程等待。
方法區的大小不是固定的,一般會在運行時JVM會根據運行情況作出調整,但JVM實現者都留有接口可供調節,比如最大和最小值。
我們都知道堆是被垃圾回收器回收的,在方法區的類也是會被回收的,類也有他的生命周期,當某個類不再被參考之后也就沒有這個類的引用和對象了那么他的存在就沒有意義了,只是站著內存,回收器就會按照一定的算法將它卸載了。對于一個類信息的表述都是用全名的,在方法區中也有其他信息,比如常量池,屬性和方法信息,還有就是指向堆中的 ClassLoader和Class 的參考。我大家都熟悉的就是類的靜態變量,也放在方法區的由所有的對象實例共享。
可以想象在方法區內放置了大量的二進制的Class信息,為了加快訪問速度,JVM實現者都會維護一個方法表,記得讀研一的時候中間件老師講過這些東西是結合指針講的C++ 的內存模型。另外注意的就是方法表只針對可實例化的類,對抽象類和接口沒有意義。
每個JVM實例都只有一個堆,所有的線程共享其中的對象,這才出現了多線程安全問題。JVM有new 對象的指令但沒有釋放對象的指令,當讓這些指令都是虛擬指令,這些對象的釋放是有 GC 來做的,GC在JVM規范中并沒有硬性的規定,有實現者設計他的實現形式和算法。
想必很多同人都想知道,對象是怎么樣在堆里表示的,其實很簡單。其實面JVM規范也沒有細致的規定對象怎么在堆里表示的,
如圖是一個參考的堆模型,具體實現可能不是這樣的,這個是HeapOfFish applet 的一個演示模型,具體內容可以看看JVM規范。當然也有很多其他的模型,這個模型的好處就是在堆壓縮的時候很方便,而在 reference 直接 point到一個對象的模型來說在堆壓縮方面是很麻煩的,因為你要考慮到方法區,堆,棧里可能的參考,你都要修改。對象還有一個很重要的數據結構就是方法表,方法表可以加快訪問速度,但并不是說所有的JVM實現都有。
堆中的每個對象都有指向方法區的指針,而自己主要保留對象屬性信息,如圖:
看一個方法區鏈接的例子,看看一個類是怎么加載進來,怎么鏈接初始化的:
有一 Salutation 類
class Salutation {
private static final String hello = "Hello, world!";
private static final String greeting = "Greetings, planet!";
private static final String salutation = "Salutations, orb!";
private static int choice = (int) (Math.random() * 2.99);
public static void main(String[] args) {
String s = hello;
if (choice == 1) {
s = greeting;
}
else if (choice == 2) {
s = salutation;
}
System.out.println(s);
}
}
Assume that you have asked a Java Virtual Machine to run Salutation. When the virtual machine starts, it attempts to invoke the main() method of Salutation. It quickly realizes, however, that it canít invoke main(). The invocation of a method declared in a class is an active use of that class, which is not allowed until the class is initialized. Thus, before the virtual machine can invoke main(), it must initialize Salutation. And before it can initialize Salutation, it must load and link Salutation. So, the virtual machine hands the fully qualified name of Salutation to the primordial class loader, which retrieves the binary form of the class, parses the binary data into internal data structures, and creates an instance of java.lang.Class.
常量池里的內容:
Index
|
Type
|
Value
|
1
|
CONSTANT_String_info
|
30
|
2
|
CONSTANT_String_info
|
31
|
3
|
CONSTANT_String_info
|
39
|
4
|
CONSTANT_Class_info
|
37
|
5
|
CONSTANT_Class_info
|
44
|
6
|
CONSTANT_Class_info
|
45
|
7
|
CONSTANT_Class_info
|
46
|
8
|
CONSTANT_Class_info
|
47
|
9
|
CONSTANT_Methodref_info
|
7, 16
|
10
|
CONSTANT_Fieldref_info
|
4, 17
|
11
|
CONSTANT_Fieldref_info
|
8, 18
|
12
|
CONSTANT_Methodref_info
|
5, 19
|
13
|
CONSTANT_Methodref_info
|
6, 20
|
14
|
CONSTANT_Double_info
|
2.99
|
16
|
CONSTANT_NameAndType_info
|
26, 22
|
17
|
CONSTANT_NameAndType_info
|
41, 32
|
18
|
CONSTANT_NameAndType_info
|
49, 34
|
19
|
CONSTANT_NameAndType_info
|
50, 23
|
20
|
CONSTANT_NameAndType_info
|
51, 21
|
21
|
CONSTANT_Utf8_info
|
"()D"
|
22
|
CONSTANT_Utf8_info
|
"()V"
|
23
|
CONSTANT_Utf8_info
|
(Ljava/lang/String;)V"
|
24
|
CONSTANT_Utf8_info
|
([Ljava/lang/String;)V"
|
25
|
CONSTANT_Utf8_info
|
<clinit"
|
26
|
CONSTANT_Utf8_info
|
<init"
|
27
|
CONSTANT_Utf8_info
|
"Code"
|
28
|
CONSTANT_Utf8_info
|
"ConstantValue"
|
29
|
CONSTANT_Utf8_info
|
"Exceptions"
|
30
|
CONSTANT_Utf8_info
|
"Greetings,planet!"
|
31
|
CONSTANT_Utf8_info
|
"Hello, world!"
|
32
|
CONSTANT_Utf8_info
|
"I"
|
33
|
CONSTANT_Utf8_info
|
"LineNumberTable"
|
34
|
CONSTANT_Utf8_info
|
"Ljava/io/PrintStream;"
|
35
|
CONSTANT_Utf8_info
|
"Ljava/lang/String;"
|
36
|
CONSTANT_Utf8_info
|
"LocalVariables"
|
37
|
CONSTANT_Utf8_info
|
"Salutation"
|
38
|
CONSTANT_Utf8_info
|
"Salutation.java"
|
39
|
CONSTANT_Utf8_info
|
"Salutations, orb!"
|
40
|
CONSTANT_Utf8_info
|
"SourceFile"
|
41
|
CONSTANT_Utf8_info
|
"choice"
|
42
|
CONSTANT_Utf8_info
|
"greeting"
|
43
|
CONSTANT_Utf8_info
|
"hello"
|
44
|
CONSTANT_Utf8_info
|
"java/io/PrintStream"
|
45
|
CONSTANT_Utf8_info
|
"java/lang/Math"
|
46
|
CONSTANT_Utf8_info
|
"java/lang/Object"
|
47
|
CONSTANT_Utf8_info
|
"java/lang/System"
|
48
|
CONSTANT_Utf8_info
|
"main"
|
49
|
CONSTANT_Utf8_info
|
"out"
|
50
|
CONSTANT_Utf8_info
|
"println"
|
51
|
CONSTANT_Utf8_info
|
"random"
|
52
|
CONSTANT_Utf8_info
|
"salutation"
|
As part of the loading process for Salutation, the Java Virtual Machine must make sure all of Salutationís superclasses have been loaded. To start this process, the virtual machine looks into Salutationís type data at the super_class item, which is a seven. The virtual machine looks up entry seven in the constant pool, and finds a CONSTANT_Class_info entry that serves as a symbolic reference to class java.lang.Object. See Figure 8-5 for a graphical depiction of this symbolic reference. The virtual machine resolves this symbolic reference, which causes it to load class Object. Because Object is the top of Salutationís inheritance hierarchy, the virtual machine and links and initializes Object as well.
3.5 操作數棧
操作數棧是Java運行時的核心棧,看看 i+j 的一個簡單運算,
iload_0
iload_1
iadd
istore_2
以上是四個JVM指令,完成 i+j并把結果保存到k中,如圖示:
在堆中不可能分配一個原始類型的空間放值,而是先用對象封裝才能存在堆空間中,帶Java棧中也不可能放對象,而只有原始類型和參考類型。上次有人爭議數組放在何處?在Java中數組和對象是同等地位的,都放在堆中而他的參考是放在棧里,JVM有對應的指令比如 newarray, anewarray等。
3.6 本地方法棧
想必很多人用過 JNI 結束,Java是不提倡這么做的,而且在這放的設計和實現上,個人覺得不是那么好,至少他比不那么方便,所以很少見應用開發者去寫些 Native 方法,每次你去看Java原代碼是你經常看到native 方法,也看到JDK下的 DLL 文件,大部分JVM都是用C或C++寫的。前面也提過,這樣就破壞了Java的平臺獨立性,在本地方法運行的時候也有專門的棧去處理。Java在執行本地方法的時候暫時放棄Java stack 的操作,轉向本地方法,本地方法有自己的棧或堆的處理方式,Java在執行本地方法時會在本地棧和Java棧之間切換,如圖:
a thread first invoked two Java methods, the second of which invoked a native method. This act caused the virtual machine to use a native method stack. In this figure, the native method stack is shown as a finite amount of contiguous memory space. Assume it is a C stack. The stack area used by each C-linkage function is shown in gray and bounded by a dashed line. The first C-linkage function, which was invoked as a native method, invoked another C-linkage function. The second C-linkage function invoked a Java method through the native method interface. This Java method invoked another Java method, which is the current method shown in the figure,做過JNI開發的朋友應該了解Java和C,C++ 是如何交互的,這些都是執行引擎的事。
3.7 執行引擎
執行引擎,應該是JVM的核心了,一般我把它看作指令集合。JVM規范詳細的描述了每個指令的作用,但沒有進一步描述如何實現,還由實現廠商自己設計,可以用解釋的方式,JIT方式,編譯本地代碼的方式,或者幾者混合的方式,當然也可用一些新的不為我們知道的技術。
每一個線程都有一個自己的執行引擎,據我了解JVM指令用一個字節表示,也就是JVM最多有256個指令,目前JVM已有160(目前可能多于這些)個指令。就有這百十個指令組成了我的系統,JVM指令一般只有操作碼沒有操作數,一般操作數放在常量池和Java棧中,設計指令集的最重要的目標應該是平臺獨立性,同時在驗證bytecode也比較方便。有些指令的操作都是基于具體類型的,有的就沒有比如 goto,如圖
指令前綴表示操作類型, 可見Java編譯器和JVM對類型操作要求很嚴格,為何使指令盡量用一個字節表示,很多類型如 byte, char 都沒有直接運算,而是在運算前把他們轉換成整型。
執行過程的在某一時刻內容如圖:
在幀里的局部變量有四個分別都是 reference,都指向不同的對象,有的時候我們編程當操作完成,最好把 o, greeter ,c, gcl 這四個參考付為 null,這樣他們指向的對象不可達,對象不可達,他們在方法區的類信息也不可達,類信息不可達,堆中的 Class 對象不可達,你可以看到圖中的所有對象,類信息都是可回收狀態,這樣GC某個時刻就可以釋放了這些內存了。
寫方法時我們希望 try cache 起來,當然也有需要在后面 加上 finally,我們都知道在finally 里的代碼被執行玩前所有的 return 操作都會壓入棧里,等 finally塊執行完了再彈出返回,如:
static boolean test(boolean bVal) {
while (bVal) {
try {
return true;
}
finally {
break;
}
}
return false;
}
可以看出這個方法的執行以返回 false 告終。
4 類文件結構
讀者可以用 UltrEdit 之類的編輯工具打開一個 class 文件查看,每個類文件都包含如下內容如圖:其中 u2 表示兩個字節大小,u4表示四個字節大小。
如第一行所示,每個類文件都以幻數開頭固定為(0xCAFEBABE),用UltraEdit 等編輯工具可以直接看到,當然你也可以看到很多如圖所示的屬性和方法的描述:
常量池是以類型來劃分不同的表格的,如CONSTANT_Double_info 表就只是double 常量值,CONSTANT_Class_info表存儲參考信息,比如類,接口,屬性和方法的符號參考,類文件的核心數據結構就是常量池了,而所有的類,屬性和方法的描述以及值都是通過 index 到常量池獲得的,所以常量池是個很重要的概念,有興趣的朋友可進一步參考JVM規范。
5 線程同步
對多線程的支持是java 語言的一大亮點,java 實現線程同步的方式是monitor,具體有兩種方式:互斥和協同,每個對象都有一把鎖,通過 lock 和 unlock 實現互斥的操作,Object類有 wait notify 等這樣的方法可以實現線程為實現一個共同的目標而協同工作。monitor就像一個建筑有一個特殊的房間每次只允許一個線程訪問。都知道線程同步有一個重要的概念就是臨界區,任何一個線程要想進入臨界區就必須獲得monitor,如果線程發現monitor被其他線程獲得,此線程必須排隊,等到他前面的線程也退出了 monitor 的占有,則此線程才能操作 monitor 的臨界區,當然也有優先級 的問題。此圖說明了多個線程協作工作的過程。
前面介紹過,堆和方法區是被線程共享的,這兩大塊要做同步,而對于局部變量是由線程獨有的所以不存在同步問題,Java API 中有一個很重要也很簡單的類就是 ThreadLocal,他是用 Theard key 來標識變量,是非局部變量局部化的一個很好的例子。
對象鎖的概念大家都很熟悉了,Java中通過這個鎖有兩種方式,一種是同步塊,一種是同步方法,具體的內容請看看java 多線程編程的書。
6 垃圾回收機制
堆里聚集了所有由應用程序創建的對象,JVM也有對應的指令比如 new, newarray, anewarray和multianewarray,然并沒有向 C++ 的 delete,free 等釋放空間的指令,Java的所有釋放都由 GC 來做,GC除了做回收內存之外,另外一個重要的工作就是內存的壓縮,這個在其他的語言中也有類似的實現,相比 C++ 不僅好用,而且增加了安全性,當然她也有弊端,比如性能這個大問題。
垃圾回收的算法有很多,不過怎么樣,任何一個算法都要做兩件事情,一是識別垃圾對象,二是清理內存以備運行程序使用。區分活著的對象和垃圾對象的主要方法就是參考計數和追蹤,參考計數就是每個對象都維護一個引用 count,而每次追蹤都去標記對象,當一次跟蹤完成沒有被標記的對象就是不可到達的那就是垃圾了。參考計數的方式現在已經很少JVM實現用了,因為對于 child, parent 的內部參考,count 永遠不會是0,也就是不會是垃圾對象,而且每次修改引用都要加減 count。而跟蹤是從根節點開始掃描所有對象,并作標記。知道那些對象是垃圾了之后,就要釋放了,有兩種方式一個是壓縮,一個是拷貝,不管哪種方式改變了對象的物理位置,就要修改他的參考值,每個對像都有一個 finalize 方法,一般我們不去 override 他,他是在回收時期執行,但并不是所有的對象的 finalize 方法都會被執行,具體內容請看 《inside in java machine》.
7 總結
本文結合作者的一些觀點和幾年的開發實踐,談談對JVM規范的看法,由于完稿倉促,也沒多少時間整理,如果有什么問題可以進一步討論,本人編譯器這方面也是比較薄弱,真正按照JVM規范實現虛擬機還有很長的路要走,可能在將來的某天有機會進一步研究一下。多謝各位的指導和幫助,我會陸續在Blog里發些文章。
8 參考資料
1)inside in java machine (想要的請點擊下載)
2)http://www.tkk7.com/Jack2007/archive/2008/05/23/202485.html《Java虛擬機深入研究》
3)http://www.tkk7.com/Jack2007/archive/2008/05/21/202018.html
4)http://www.tudou.com/programs/view/hN_4sQJMoFQ/
5)http://www.tkk7.com/Jack2007/archive/2008/04/11/192288.html
6)http://developer.51cto.com/art/200805/73338.htm
本博客為學習交流用,凡未注明引用的均為本人作品,轉載請注明出處,如有版權問題請及時通知。由于博客時間倉促,錯誤之處敬請諒解,有任何意見可給我留言,愿共同學習進步。