JVM:Java Virtual Machine,Java虛擬機。

JVM:Java Virtual Machine,Java虛擬機。

        作用:由類裝載器裝載class字節碼文件,通過執行引擎執行被裝載類中方法中的指令。
        生命週期:啓動一個Java程序,就會創建一個虛擬機實例,該程序關閉,虛擬機實例隨之消亡。
                            虛擬機實例通過調用某個初始類的main方法來運行一個Java程序.

JVM模塊介紹:

        1:類裝載子系統:根據給定的類名或者接口名裝載class字節碼文件。
        2:執行引擎:負責執行被裝載類中的方法的指令。
        3:方法區跟堆:每個虛擬機實例都有一個方法區和一個堆。【他們由該虛擬機實例中所有線程共享。】

                方法區:當虛擬機裝載一個class文件時,他會從這個class文件包含的二進制信息中解析類型數據,然後他把這些類型數據放到方法區中,方法區包含所有的class和static變量。
                        解析類型信息,都有哪些類型信息呢?:
                        1:基本信息
                                這個類型的全限定名
                                這個類型的直接超類的全限定名(除非這個類型是java.lang.Object,它沒有超類)
                                這個類型是類類型還是接口類型
                                這個類型的訪問修飾符(public、abstract或final的某個子集)
                                任何直接超接口的全限定名的有序列表
                        2:其它信息
                                該類型的常量池
                                字段信息
                                方法信息
                                除了常量以外的所有類(靜態)變量
                                一個到類ClassLoader的引用
                                一個到Class類的引用

                堆:當程序運行時,虛擬機會把所有該程序在運行時創建的對象都放到堆中。

        4:棧:每當啓動一個新線程時,Java虛擬機都會爲它分配一個Java棧。Java棧以幀爲單位保存線程的運行狀態。
                   虛擬機只會直接對Java棧執行兩種操作:以幀爲單位的壓棧和出棧。
                   Java棧則總是存儲該線程中Java方法調用的狀態——包括它的局部變量,被調用時傳進來的參數、返回值,
                   以及運算的中間結果等等。

                Java棧是由許多棧幀(stack frame)組成的,一個棧幀包含一個Java方法調用的狀態。當線程調用一個Java方法時,虛擬機
                壓入一個新的棧幀到該線程的Java棧中,當該方法返回時,這個棧幀被從Java棧中彈出並拋棄。

                每個棧中的數據(原始類型和對象引用)都是私有的,其他棧不能訪問。

                Java虛擬機沒有寄存器,其指令集使用Java棧來存儲中間數據。這樣設計的原因是爲了保持Java虛擬機的指令集儘量緊湊。
                同時也使於Java虛擬機在那些只有很少通用寄存器的平臺上實現。另外,Java虛擬機的這種基於棧的體系結構,也有助於運行時某些虛擬機實現的動態編譯器和即時編譯器的代碼優化。


                棧空間的生命週期:
                        開闢:線程被創建時,分配棧空間。
                        回收:棧空間隨着線程的終止而釋放。
                        操作模式:棧中的數據佔內存大小在編譯時是確定的,有兩個基本操作:入棧和出棧,操作規則是:LIFO(Last In Fast Out),後進先出。

                        StackOverflowError:如果在線程執行的過程中,棧空間不夠用,那麼JVM就會拋出此異常,這種情況一般是死遞歸造成的

                堆空間的生命週期:
                        開闢:程序運行時(進程創建時)創建堆空間。
                        回收:當停止了對一個對象的引用,過一段時間後,GC會自動回收這個對象所佔的內存。
                        操作模式:動態內存空間,棧中的數據佔內存大小和初始值在運行時確定。

                堆跟棧也可以理解成JVM在建立一個進程或線程時爲它們分配的存儲區域。

Java類加載的全過程,是加載、連接(驗證、準備、解析)和初始化這三個階段的過程。

(1)加載階段是類加載過程的一個階段。在加載階段,虛擬機需要完成以下三件事情:
        1:通過一個類的全限定名來獲取定義此類的二進制字節流(Class文件)
        2:將這個字節流所代表的靜態存儲結構轉化爲方法區的運行時數據結構
        3:在Java堆中生成一個代表這個類的java.lang.Class對象,作爲方法區這些數據的訪問入口。 

        在這三件事情中,通過一個類的全限定名來獲取定義此類的二進制字節流這個動作是在Java虛擬機外部來實現的,以便讓應用程序自己決定如何去獲取所需要的類。實現這個動作的代碼模塊被稱爲“類加載器”。

(2)連接——指向驗證、準備、以及解析(可選)。
        1:驗證  確保被導入類型的正確性。
        2:準備  爲類變量分配內存,並將其初始化爲默認值。
        3:解析  把類型中的符號引用轉換爲直接引用。

(3)初始化——把類變量初始化爲正確初始值。

實例分析:

class Person {
	private String name;

	public Person(String name) {
		this.name = name;
	}

	public void say() {
		System.out.println("My Name is:" + this.name);
	}
}

public class Demo {
	public static void main(String[] args) {
		Person p1 = new Person("小強");
		p1.say();
	}
}

/*
1:執行java Demo,創建一個Java虛擬機實例(進程),該進程首先根據classpath找到Demo.class文件。
讀入該文件中的二進制信息,然後把Demo類的類信息存放到運行時數據區的方法區中。類加載完成。

2:Java虛擬機定位到方法區中Demo類的main方法的字節碼,開始執行他的指令。
那麼在main方法中,第一條語句是:Person p1 = new Person("小強");
意思是讓虛擬機創建一個Person類的實例,然後讓引用變量p1引用這個實例。

1)首先虛擬機會到方法區中尋找Person類的類信息,結果沒找到。於是會立即加載Person類,並把Person類的類信息存放到方法區中。

2)成功加載了Person類,緊接着爲Person類的實例對象在堆中分配內存,實例對象持有着指向方法區的Person類的類信息的引用。
這裏所說的引用,實際上指的是Person類的類信息在方法區中的內存地址。

3)p1是main方法中的局部變量,所以會被添加到執行main方法的主線程的棧中。賦值運算符"="會把p1引用變量指向堆區中的Person實例對象。也就是說,p1持有指向Persons實例的引用。

4)當虛擬機執行p1.say()方法時,虛擬機會根據局部變量p1持有的引用,定位到堆區中的Person實例對象,
再根據Person類實例對象持有的引用,定位到方法區中Person類的類型信息,從而獲得say()方法的字節碼,
接着執行say()方法中包含的指令。
*/

附:

Java內存保護的方式

1:不使用指針,使用引用。
2:自動垃圾收集器(GC)。
3:嚴格的數組邊界檢查,數組越界會拋出數組腳標越界異常。
4:對象引用檢查,使用引用的時候確保這些引用不爲空值,否則拋出空指針異常。

Java體系結構的代價

1:程序執行速度相對於本地平臺的程序來說,相對較慢。
2:內存管理,垃圾收集器使程序更加健壯,但你無法GC什麼時候開始回收垃圾,無法確認GC是否開始收集垃圾,也無法確認收集垃圾要持續多長時間。
3:因爲Java程序是動態連接的,從一個類到另一個類的引用是符號化的。在靜態連接的可執行程序中,類之間的引用只是直接的指針或者偏移量。
相反,在Java class文件中,指向另一個類的引用通過字符串清楚地表明瞭所指向的這個類的名字,一個class文件的符號信息,以及字節碼指令集和Java語言之間的密切關係,
使得把class文件逆向編譯爲Java源碼相當容易。可以使用混淆器混淆class文件來解決這個問題。

發佈了23 篇原創文章 · 獲贊 6 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章