1. 程式人生 > >JVM虛擬機器--隨筆

JVM虛擬機器--隨筆

之前也看過很多資料,不過都自然而然的過濾了很多,下面就把自己對JVM初步的認識整理一下:

JVM全稱(Java Virtual Machine),跟我們程式設計師接觸最多的差不多就是它裡面的棧、堆、方法區,還有一個比較重要的就是程式計數器

程式計數器是什麼呢?

程式計數器是一塊較小的記憶體空間,它的作用可看作是當前執行緒所執行的位元組碼的行號指示器。位元組碼直譯器工作時就是根據改變計數器的值來選定下一條需要執行的位元組碼指令,例如:分支、迴圈、跳轉、異常處理、執行緒恢復。                                       在java多執行緒的情況下,執行緒的執行是根據處理器來分配時間的,在特定時刻處理器只能處理一個執行緒中的指令,而多個執行緒之間是來回切換執行的,每個執行緒都有一個屬於自己的程式計數器,來保證執行緒切換後恢復到正確的執行位置。程式計數器記錄的是正在執行的虛擬機器位元組碼指令,而執行的不是java方法而是native方法時,則記錄的是undefined,此區域在java規範中是一個唯一沒有規定outOfMemoryError異常的區域。多個執行緒的程式計數器之間是互不影響的。像這種記憶體區域,我們稱之為執行緒私有的記憶體。

棧是什麼東東呢?

棧是描述java方法執行的記憶體模型。每個方法執行的時候都會建立一個棧幀,用來儲存區域性變量表、動態連結、方法出口等資訊,鑑於此,所有它也是執行緒私有的。

區域性變量表儲存的是在編譯期可知的基本資料型別(八大基本型別:byte/boolean/short/char/int/float/double/long)、物件引用(reference型別)、returnAddress型別。其中64位的long/double型別佔用了兩個區域性變數空間(slot),其它型別的只佔用一個區域性變數空間。區域性變量表的空間在編譯期完成分配,方法執行期間不會改變區域性變量表的空間大小。

此區域在java虛擬機器規範中規定了兩種異常:

                                                                                                                            1.如果執行緒請求的棧深度大於虛擬機器所允許的深度,則會丟擲stackoverflowError異常。                                                             2.如果虛擬機器棧可以動態擴充套件(大部分虛擬機器棧目前都可以支援動態擴充套件),擴充套件時沒有申請到足夠記憶體時則會丟擲OutOfMemoryError異常

堆是什麼呢?

java堆是java虛擬機器中最大的一塊記憶體。它被所有執行緒所共享。它的唯一目的就是存放例項物件,所有的例項物件都在此分配記憶體。同時也是垃圾收集器主要管理的區域。

方法區是什麼東東?

方法區與java堆一樣,都是各執行緒所共享的記憶體區域。用於存放被java虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯的程式碼等資料

執行時常量池是方法區的一部分。Class檔案中除了包含基本的類資訊外還有一項資訊是常量池(Constant pool table),用於儲存編譯器生成的字面量和符號引用,這部分內容將在類載入後存放到方法區中的執行時常量池。


我們在建立一個物件的時候,是怎樣進行訪問的呢?與以上所說的幾個記憶體區又是怎樣關聯聯絡的呢?

Object o = new Object(); 在方法區執行改程式碼時,Object o這部分的語義將會作為一個reference型別的資料被反映到java棧的本地變量表中。而new Object()這部分語義將會被反映到java堆中,形成了一塊儲存Object型別所有例項資料值的結構化記憶體。在java堆中還生成了包含此物件型別資料的引用地址資訊(必須有的),而此物件型別資料的例項值則在方法區中存放。

這裡值得一說的是reference這個引用物件型別在java規範中並沒有規定通過哪種方式去定位及訪問到java堆中物件的位置,不同的虛擬機器實現訪問方式也是有所不同的,這兩主流的訪問方式有兩種:     使用控制代碼;直接指標         

  第一種,通過控制代碼訪問方式,reference引用型別中存有物件的控制代碼地址資訊,在java堆中會劃分出一塊記憶體為控制代碼池,而控制代碼物件存有物件例項資料的地址資訊及型別資料的地址資訊,請看下圖:


第二種,通過直接指標方式訪問,在java堆物件佈局中就需要考慮如何訪問物件型別資料的相關資訊,而reference中儲存的就是物件例項資料的地址資訊,請看下圖:


既然有兩種訪問方式,那麼問題來了,這兩種使用哪種好呢?各自的特點是什麼?

第一種:控制代碼訪問,reference中儲存的物件的控制代碼地址資訊比較穩定,在物件被移動時(垃圾收集時,物件就會移動),改變的是控制代碼物件中物件例項資料的地址資訊,而reference不會改變。

第二種:直接指標訪問,速度快效率高。因為reference中儲存的直接是物件的地址資訊,少了一步定位,即節省了一次指標定位的時間開銷,由於物件的訪問在java中是非常頻繁的,所有積少成多也是非常可觀的,大大節省了執行成本。例如:Sun HotSpot


今天就說這麼多,後續如果有進一步的認識會再次跟大家一塊分享,,同時也希望大家如果有不同的見解能夠提出來,共同進步!得意得意得意