Java虛擬機一直是Java的重難點,一方面因爲系統封裝得太好,你日常寫程序的時候幾乎感受不到它的存在,另外一方面瞭解必要的Java虛擬機工做原理才能對真實工做環境下的bug進行對症下藥,另外虛擬機這一部分也一直是面試考官愛問的問題。因而這篇博客就針對Java虛擬機的各個知識點進行概括。程序員
程序計數器是當前線程執行的字節碼的行號指示器,線程私有,獨立存儲面試
Java虛擬機棧是線程私有,與Java的方法執行模型有關,描述Java方法執行的內存模型:方法執行時建立棧幀用於儲存局部變量表等信息,方法調用返回對應棧幀再虛擬機中的入棧出棧。算法
既然是棧那麼深度就是必定的,若線程請求棧深度大於虛擬機所規定的深度,則拋出StackOverflowError異常。若虛擬機棧請求擴展時沒法申請到足夠的內存,則拋出OOM異常。數組
就是Native方法所用到的棧,與虛擬機棧做用相似。佈局
Java堆是被全部線程共享的一塊內存區域,屬於線程共享區,在虛擬機啓動時建立。它主要做用是存放對象實例和進行垃圾收集管理。編碼
方法區也是各個線程共享的內存區域,用於儲存已被虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼等數據。線程
運行時常量池其實屬於方法區,它主要用於存放編譯期生成的各類字面量和符號引用,而且具備動態的特色。翻譯
指針碰撞指針
若Java堆中的內存都是規整的,用過的內存都在左邊,沒用過的都在右邊,中間指針指向臨界點,分配內存就很簡單,只用把指針往右移動和待分配對象同樣的內存區域就好了。cdn
空閒列表
若是內存不是規整的,用過的和沒用過的內存交錯在一塊兒,就不能使用指針碰撞了,須要維護一個列表記錄可用的內存塊,分配內存時就從列表中找一塊足夠大的內存記錄下來。
儲存對象自身的運行時數據,eg:哈希碼,GC分代年齡,鎖狀態標誌等。還有類型指針指向它的類元數據的指針,經過這個指針肯定這個對象是哪一個類的實例。如果Java數組則對象頭還有一塊記錄數組長度的數據。
程序代碼中所定義的各類類型的字段內容,相同寬度的字段分配到一塊兒
虛擬機經過棧上的reference數據來操做堆上的具體對象。
使用句柄
包含對象實例數據與類型數據各自的地址信息,reference中儲存的就是對象的句柄地址。句柄地址穩定,對象移動時只改變句柄中的實例數據指針,reference自己不修改。
直接指針
reference中儲存的就是對象地址,速度更快
給對象添加一個引用計數器,有一個地方引用它時,計數器值就加一,引用失效時就減一,任什麼時候刻計數值爲0的對象就死了。這個算法雖然簡單可是有一個致命的缺點就是沒法解決對象之間相互循環引用的關係。可達性分析算法應運而生。
GC Roots做爲起點向下搜索,若一個對象到GC Roots沒有引用鏈的話,則證實此對象不可用,能夠回收。搜索的對象有:
對象在沒有引用鏈通往GC Roots時,須要通過兩次標記才能真正死亡。
首先標記出全部須要回收的對象,在標記完成後統一回收,缺點是效率低下並且產生大量的內存碎片。
將內存劃分爲大小相等的兩塊,每次只使用其中的一塊,當這一塊的內存用完了,就將還存活的對象複製到另一塊上面,而後把已經使用的內存空間一次清理掉。缺點是將內存縮小爲了原來的一半,代價較高,對象存活率較高時效率低。
HotSpot實際使用(回收新生代)則是將內存劃分爲較大的Eden區和兩塊較小的Survivor區,一塊Eden區和一塊Survivor區大小比例爲8:1,垃圾回收時就將Eden區和已使用的Survivor區中還存活的對象移到另外一塊Survivor區中,因爲根據統計,98%的對象都是很快死亡的,因此按照8:1:1的比例來劃份內存明顯比1:1劃份內存效率要高不少。
標記出須要回收的對象,而後讓全部存活的對象都向一段移動,將另外一端的內存區域清除掉。
根據新生代和老年代的不一樣特色選擇不一樣的算法,新生代使用複製算法,老年代使用標記清楚或標記整理算法,虛擬機實際使用這種算法。
大對象指須要大量連續內存空間的Java對象,如很長的字符串以及數組。直接進入老年代避免頻繁的GC活動。
對象在新生代區域每熬過一次Minor GC,年齡就增長一歲(Age Count),超過15歲(默認),就會被晉升到老年代中。
若是相同年齡的對象所佔內存大於Survivor空間的一半,年齡大於等於該年齡的對象就能夠直接進入老年代。
一組以八位字節爲基礎的二進制流,各個數據項目嚴格按照順序緊湊地排列在Class文件之中,中間沒有任何分隔符。
無符號數,用來描述數字,索引引用,數量值或UTF-8編碼的字符串
表,多個無符號數+表=表,_info結尾,Class實際上就是一張表
每一個Class文件的頭4個字節,肯定這個文件是否爲一個能被虛擬機接受的Class文件。class文件的魔數是0XCAFEBABE。
緊跟魔數的四個字節肯定版本號:5,6字節爲次版本號,7,8字節爲主版本號。jdk向下兼容,不向上兼容。
緊隨主次版本號以後包含:
緊隨常量池後面,兩個字節表明訪問標誌,標識類或接口的訪問信息。如這個Class是類仍是接口,public類型等。
除了接口索引是集合外,其餘索引都只有一個,用這三個索引肯定類的繼承關係。類索引用於肯定類的全限定名,父類索引用於肯定父類的全限定名。
用於描述類或接口中聲明的變量,字段包括類級變量和實例級變量,不包括方法中聲明的局部變量,描述字段的屬性如public,static,final等用一個布爾變量表示,恰好使用一個標誌位,經過引用常量池中的常量來肯定。
與字段表類似。
Class文件,字段表,方法表均可以攜帶本身的屬性表集合,用於描述某些場景專有的信息。
操做碼長度爲一個字節,因此總數最多不超過256條。。