深入JAVA虛擬機器-第二版

weixin_34253539發表於2017-03-13

第一章 JAVA體系結構介紹

  • java應用程式可以使用兩種類裝載器,啟動(bootstrap)類裝載器和使用者自定義裝載器。
    每一個類被裝載的時候,java虛擬機器都監視著這個類,看它是被什麼裝載器裝載的,當被裝載的類引用了其他類的時候,
    會使用相同的類裝載器去裝載被引用的類。因此預設情況下只能看到被同一個類裝載器裝載的類,通過這種方法,java允許
    在java程式中簡歷多個名稱空間,每一個類載入器都有自己的名稱空間。
  • class 檔案,執行在JVM上的二進位制檔案

第三章 安全(--看完再回顧本章)

  • JAVA沙箱中類裝載體系結構是第一道防線
  • 防止惡意程式碼干涉善意程式碼
  • 守護被信任類庫的邊界
  • 將程式碼分類,確定該類程式碼可以執行些些操作
  • class 檔案檢測器
  • 檢驗位元組碼的的完整性
  • 型別資料語義檢查
  • 位元組碼驗證
  • 符號引用驗證
  • 二進位制相容
  • JVM內建安全特性
  • 型別安全的引用轉換
  • 結構化的記憶體訪問
  • 自動垃圾收集
  • 陣列邊界檢查
  • 空引用檢查
  • 禁止對記憶體進行非結構化訪問
  • 異常的結構化處理
  • 安全管理器和java api
  • 程式碼簽名和認證,將一個未簽名的class檔案通過雜湊和私鑰得到一個帶有簽名後雜湊的檔案
  • 訪問控制器

第五章 java虛擬機器

  • 生命週期,main作為函式的起點,每一個程式都執行一個虛擬機器例項上。java虛擬機器上兩種執行緒,一種是守護執行緒,另一種是非守護執行緒
    守護執行緒是由jvm自己使用的,比如垃圾回收的執行緒,而開始於main函式的執行緒是非守護執行緒。只要還有非守護執行緒在執行,虛擬機器還是存活的
    ,所有非守護執行緒退出時,虛擬機器會自動退出。

java 虛擬機器的結構體系

                             -------------------
                            |   類裝載器子系統    |           
                             -------------------
                                     |
    ------------------------------------------------------------------
    |    |        |         |              |                   |      |
    |  方法區      堆       java棧        PC暫存器            本地方法棧  |
    ------------------------------------------------------------------
          |                             |
       執行引擎    ————————————————>  本地方法介面

  • 一些點
  • 方法區和堆記憶體是由所有的執行緒共享的,虛擬機器裝載class檔案時,會把類的資料放在方法區中,當程式執行時,會把根據類資料建立的物件
    放在堆記憶體中。
  • 當新執行緒被建立時,都將得到一個PC暫存器和一個JAVA棧PC暫存器總是指向下一條被執行的指令,java棧則儲存方法的呼叫狀態。(區域性變數,引數返回值,運算中間值)
  • java 棧是有很多的棧幀組成,當執行緒呼叫一個方法的時候,,JVM將新的棧幀入棧,當函式返回時,棧幀被出棧。
  • 資料型別
  • 基本型別(float, double, byte, short, int, long, char, boolean, returnAddress)
    64 32 8 16 32 64 16
  • 引用型別(類,介面 ,陣列)

方法區

  • 方法區的資料共享,因此是必須執行緒安全的。虛擬機器允許程式設計師指定方法區的大小
  • 方法區會在記憶體中儲存類的以下資訊:
  • 型別的全限定名(包名+.+類名)

  • 此類超類的全限定名

  • 類還是介面

  • 修飾符(public ,abstruct, final)

  • 介面全限定名的有序列表

  • 型別的常量池

  • 欄位資訊(欄位名,型別,修飾符)

  • 方法資訊(方法名,型別,修飾符)

  • 一個到類ClassLoder的引用

  • 一個到Class類的引用(forName(),讓使用者得到已裝載物件的Class例項)

  • 方法位元組碼

  • 運算元棧和該方法在棧幀中區域性變數的大小

  • 異常表

  • 下面看一看流程

class Volcano(){
    public static void main(String args[]){
        Lava lava = new Lava();
        lava.flow();
    }
}
  • 例如我們要執行一個叫Volcano的類,當我們告訴JVMVolcano這個名字
  • JVM會找到並讀入Volcano.class檔案
  • 然後匯入其中的二進位制流,並把相應的資料存在方法區
  • 通過執行方法區中的位元組碼,開始執行main()方法,在執行時會一直持有指向常量池的指標
  • main方法中第一條指令是給常量池第一個類分配象的記憶體,於是在常量池中找到第一項,發現是對另一個類Lava的引用
  • 檢查方法區,看是否被裝載,發現沒有被裝載,於是查詢Lava.class檔案裝載,同樣把資訊儲存在方法區中
  • 接著指向常量池第一行的指標替換掉常量池的第一項(Lava的全限定名)-> 常量池的解析(符號引用替換成直接引用,本地指標)
  • 虛擬機器準備為Lava分配記憶體,並用這個指標訪問它的類資訊,找出類資訊中的需要為這個類分配多少堆記憶體
  • 虛擬機器確定了大小,就在堆上為物件分配記憶體,並初始化常量值。然後把物件的引用入棧,第一條指令執行完成。
  • 接下來通過這個引用呼叫flow方法

4252048-8cb9299ed8739788.png
兩種堆設計
  • java一個虛擬例項中只有一個堆空間,所有執行緒共享

  • java 只有在堆中分配記憶體的指令,沒有釋放的指令,垃圾收集器會負責堆和方法區的記憶體回收

  • 堆和方法區一樣也不是一個連續的記憶體區,是可擴充套件的

  • 堆上的物件還有一部分資料,是物件鎖(互斥鎖),請求可以追加,請求幾次,必須釋放幾次,例如請求了4次,只釋放了3次,那還是持有這個物件鎖。

  • 堆上物件還有一部分資料與垃圾收集器有關,垃圾回收器必須跟蹤每個物件。

  • 陣列是一個物件,總是儲存在堆中

  • PC(程式計數器)暫存器,在每個執行緒中有一個,它有一個字的大小,儲存一個本地指標,總是指向下一條將要執行的指令。

java 棧

4252048-e9714be7be61b6d1.png
過程
  • 每當啟用一個執行緒,JVM會為它分配一個java 棧。
  • 棧幀的組成
  • 區域性變數區 -> 一個陣列,以字(32bit)為單位,型別int,float,returnAddress的值再陣列中佔一項,byte,short,char會被轉化成int,也佔一項,double,long佔2項。任何一個例項方法(非static),的陣列第一項都是物件本身的reference.
  • 運算元棧 -> byte,short,char會被轉化成int,和上面一樣,只有在存回堆中是會被轉化為原來型別。他也是一個陣列,但是按照棧操作來訪問。由於java虛擬機器沒有暫存器,java的指令是通過運算元棧中取得運算元的。
  • 棧幀資料,支援常量池的解析正常方法返回和異常派發機制的資料。
    為了處理執行期的異常退出情況,幀資料區儲存一個對此方法的異常表的引用。當方法丟擲異常,會在異常表中查詢對應的異常,如果找到了匹配的catch語句,就會交給ccatch中的程式碼處理,如果沒有則異常中止。
  • 兩站不同的虛擬機器實現的幀分配


    4252048-18ea3b6b1b879574.png
4252048-1aa586ff0ba688d1.png
  • 本地方法棧,取決於設計者的實現

執行引擎(沒看太明白)

  • 執行中java程式的每一個執行緒都是一個獨立的虛擬機器執行引擎的例項。
  • 從執行緒的生命週期開始,它要麼在執行位元組碼,要麼在執行native方法。
  • 指令集,指令集關注的中心是運算元棧。運算元棧中的數值必須按照適合他們型別的方式使用。比如壓入棧4個int,卻把他們當做兩個long來做操作,是非法的 。

第六章 JAVA class檔案 (粗略看)

  • java class檔案是對java程式二進位制檔案的精確定義。一個class檔案中指包括一個class 或者interface.

  • class檔案不一定與java語言有關,別的語言也可以編譯成class檔案在虛擬機器上執行。

  • 4252048-f1f3cca716e19ffd.png
  • class 檔案組成
  • magic ,每個class檔案的前四個位元組 0xCAFEBABE,用來分辨是否是class檔案。
  • minor_version major_version b版本號。
  • constant_pool_count 常量池的數量,constant_pool 常量池
  • access_flags 類的修飾資訊,public ,private ,static 等
  • this_class 指向常量池的索引
  • super_class 超類常量池索引
4252048-5a3c50e17675d782.png

第七章 型別的生命週期

  • 類主動裝載的時機
  • 建立例項時(new, 反射,克隆,反序列化)
  • 呼叫類中的靜態方法
  • 使用類或介面的靜態欄位或者對欄位賦值(final 修飾的靜態變數除外,它被初始化為一個編譯時的常量表示式)
  • 呼叫api反射方法
  • 初始化子類
  • 含有main函式的類
  • 所有類的初始化都要求它的超類在此之前初始化了。(介面不是)
  • 裝載
  • 通過完全限定名產生一個二進位制流
  • 解析二進位制檔案
  • 建立一個該型別Class例項
  • 初始化
  • 所有類變數初始化語句和型別的靜態初始化器都被java編譯器收集到一起放在一個特殊的方法<clinit>中,並非所有的類都有這個方法。
  • 如果多個執行緒需要初始化一個類,僅僅允許一個執行緒來執行初始化,其他執行緒需要等待。完成後需要通知其他等待執行緒。
物件生命週期
  • 例項化
  • new

  • reflect.newInstance()

  • clone()

  • ObjectInputStream.getObject

  • 堆中分配記憶體,然後賦予例項變數初始值

  • 型別當沒有引用的時候會被解除安裝,同樣通過垃圾回收器。

第八章 連結模型

相關文章