摘要:于是這篇博客就針對虛擬機的各個知識點進行歸納。若虛擬機棧請求擴展時無法申請到足夠的內存,則拋出異常。類索引用于確定類的全限定名,父類索引用于確定父類的全限定名。字節碼指令操作碼長度為一個字節,所以總數最多不超過條。
Java虛擬機一直是Java的重難點,一方面由于系統封裝得太好,你平常寫程序的時候幾乎感覺不到它的存在,另一方面了解必要的Java虛擬機工作原理才能對真實工作環境下的bug進行對癥下藥,另外虛擬機這一部分也一直是面試考官愛問的問題。于是這篇博客就針對Java虛擬機的各個知識點進行歸納。
一.Java內存區域 運行時數據區域 程序計數器程序計數器是當前線程執行的字節碼的行號指示器,線程私有,獨立存儲
Java虛擬機棧Java虛擬機棧是線程私有,與Java的方法執行模型有關,描述Java方法執行的內存模型:方法執行時創建棧幀用于儲存局部變量表等信息,方法調用返回對應棧幀再虛擬機中的入棧出棧。
既然是棧那么深度就是一定的,若線程請求棧深度大于虛擬機所規定的深度,則拋出StackOverflowError異常。若虛擬機棧請求擴展時無法申請到足夠的內存,則拋出OOM異常。
本地方法棧就是Native方法所用到的棧,與虛擬機棧作用類似。
Java堆Java堆是被所有線程共享的一塊內存區域,屬于線程共享區,在虛擬機啟動時創建。它主要作用是存放對象實例和進行垃圾收集管理。
方法區方法區也是各個線程共享的內存區域,用于儲存已被虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯后的代碼等數據。
運行時常量池運行時常量池其實屬于方法區,它主要用于存放編譯期生成的各種字面量和符號引用,并且具有動態的特點。
new關鍵字的創建流程檢查指令的參數能否在常量池中定位到一個類的符號引用
檢查是否已經加載解析和初始化
從Java堆中劃分內存給新生對象,使用CAS保證分配的原子性
將內存空間初始化為零值
對對象進行設置,存放在對象頭中
執行
指針碰撞
若Java堆中的內存都是規整的,用過的內存都在左邊,沒用過的都在右邊,中間指針指向臨界點,分配內存就很簡單,只用把指針往右移動和待分配對象一樣的內存區域就行了。
空閑列表
如果內存不是規整的,用過的和沒用過的內存交錯在一起,就不能使用指針碰撞了,需要維護一個列表記錄可用的內存塊,分配內存時就從列表中找一塊足夠大的內存記錄下來。
對象的內存布局 對象頭儲存對象自身的運行時數據,eg:哈希碼,GC分代年齡,鎖狀態標志等。還有類型指針指向它的類元數據的指針,通過這個指針確定這個對象是哪個類的實例。若是Java數組則對象頭還有一塊記錄數組長度的數據。
實例數據程序代碼中所定義的各種類型的字段內容,相同寬度的字段分配到一起
對象訪問定位虛擬機通過棧上的reference數據來操作堆上的具體對象。
訪問方式使用句柄
包含對象實例數據與類型數據各自的地址信息,reference中儲存的就是對象的句柄地址。句柄地址穩定,對象移動時只改變句柄中的實例數據指針,reference本身不修改。
直接指針
reference中儲存的就是對象地址,速度更快
二.垃圾收集器與內存分配策略 引用計數算法給對象添加一個引用計數器,有一個地方引用它時,計數器值就加一,引用失效時就減一,任何時刻計數值為0的對象就死了。這個算法雖然簡單但是有一個致命的缺點就是無法解決對象之間相互循環引用的關系。可達性分析算法應運而生。
可達性分析算法GC Roots作為起點向下搜索,若一個對象到GC Roots沒有引用鏈的話,則證明此對象不可用,可以回收。搜索的對象有:
虛擬機棧中引用的對象
方法區中靜態屬性引用的對象
方法區中常量引用的對象
本地方法棧中Native 方法引用的對象
對象的回收經歷對象在沒有引用鏈通往GC Roots時,需要經過兩次標記才能真正死亡。
對象在進行可達性分析后如果沒有與GC Roots相連接的引用鏈,會被第一次標記并篩選,若對象沒有覆蓋finalize方法或者已經調用過了則不會調用finalize。如果需要調用finalize方法,則對象被放在F-Queue隊列中,等待線程執行。
對象如果想存活下去,finalize方法是最后的機會,否則GC對F-Queue隊列進行第二次標記后對象真正死亡。
垃圾回收算法 標記-消除算法首先標記出所有需要回收的對象,在標記完成后統一回收,缺點是效率低下而且產生大量的內存碎片。
復制算法將內存劃分為大小相等的兩塊,每次只使用其中的一塊,當這一塊的內存用完了,就將還存活的對象復制到另外一塊上面,然后把已經使用的內存空間一次清理掉。缺點是將內存縮小為了原來的一半,代價較高,對象存活率較高時效率低。
HotSpot實際使用(回收新生代)則是將內存劃分為較大的Eden區和兩塊較小的Survivor區,一塊Eden區和一塊Survivor區大小比例為8:1,垃圾回收時就將Eden區和已使用的Survivor區中還存活的對象移到另一塊Survivor區中,由于根據統計,98%的對象都是很快死亡的,所以按照8:1:1的比例來劃分內存明顯比1:1劃分內存效率要高很多。
標記-整理算法標記出需要回收的對象,然后讓所有存活的對象都向一段移動,將另一端的內存區域清除掉。
分代收集算法根據新生代和老年代的不同特點選擇不同的算法,新生代使用復制算法,老年代使用標記清楚或標記整理算法,虛擬機實際使用這種算法。
內存分配與回收策略 對象優先在Eden上分配 GC分類Monior GC,新生代GC,指發生在新生代的垃圾收集動作,因為Java對象大多都具備朝生夕滅的特點,所以Monior GC很頻繁,速度也很快
Major GC/Full GC,老年代GC,指發生在老年代的垃圾回收動作,一般比Monior GC慢十倍以上。
大對象直接進入老年代大對象指需要大量連續內存空間的Java對象,如很長的字符串以及數組。直接進入老年代避免頻繁的GC活動。
長期存活的對象將進入老年代對象在新生代區域每熬過一次Minor GC,年齡就增加一歲(Age Count),超過15歲(默認),就會被晉升到老年代中。
動態年齡判定如果相同年齡的對象所占內存大于Survivor空間的一半,年齡大于等于該年齡的對象就可以直接進入老年代。
三.類文件結構 Class類文件的結構一組以八位字節為基礎的二進制流,各個數據項目嚴格按照順序緊湊地排列在Class文件之中,中間沒有任何分隔符。
儲存結構無符號數,用來描述數字,索引引用,數量值或UTF-8編碼的字符串
表,多個無符號數+表=表,_info結尾,Class實際上就是一張表
魔數每個Class文件的頭4個字節,確定這個文件是否為一個能被虛擬機接受的Class文件。class文件的魔數是0XCAFEBABE。
Class文件的版本號緊跟魔數的四個字節確定版本號:5,6字節為次版本號,7,8字節為主版本號。jdk向下兼容,不向上兼容。
常量池緊隨主次版本號之后包含:
字面量文本字符串,申明為final的常量值。
符號引用
類和接口的全限定名
字段的名稱和描述符
方法的名稱和描述符
動態連接各個字段的內存信息,從常量池中獲得對應的讀出引用,再在類創建時或運行解析翻譯到具體的內存地址之中。
每一項常量都是一個表,每個表的第一位都是一個是一個u1類型的標志位,代表這個常量屬于哪種常量類型。
訪問標志緊隨常量池后面,兩個字節代表訪問標志,標識類或接口的訪問信息。如這個Class是類還是接口,public類型等。
類索引,父類索引,接口索引集合除了接口索引是集合外,其他索引都只有一個,用這三個索引確定類的繼承關系。類索引用于確定類的全限定名,父類索引用于確定父類的全限定名。
字段表集合用于描述類或接口中聲明的變量,字段包括類級變量和實例級變量,不包括方法中聲明的局部變量,描述字段的屬性如public,static,final等用一個布爾變量表示,剛好使用一個標志位,通過引用常量池中的常量來確定。
方法表集合與字段表相似。
屬性表集合Class文件,字段表,方法表都可以攜帶自己的屬性表集合,用于描述某些場景專有的信息。
字節碼指令操作碼長度為一個字節,所以總數最多不超過256條。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68628.html
摘要:本篇博客主要針對虛擬機的晚期編譯優化,內存模型與線程,線程安全與鎖優化進行總結,其余部分總結請點擊虛擬總結上篇,虛擬機總結中篇。 本篇博客主要針對Java虛擬機的晚期編譯優化,Java內存模型與線程,線程安全與鎖優化進行總結,其余部分總結請點擊Java虛擬總結上篇 ,Java虛擬機總結中篇。 一.晚期運行期優化 即時編譯器JIT 即時編譯器JIT的作用就是熱點代碼轉換為平臺相關的機器碼...
摘要:驗證過程驗證過程的目的是為了確保文件的字節流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機自身的安全。二虛擬機字節碼執行引擎虛擬機的執行引擎自行實現,可以自行制定指令集與執行引擎的結構體系。 本篇博客主要針對Java虛擬機的類加載機制,虛擬機字節碼執行引擎,早期編譯優化進行總結,其余部分總結請點擊Java虛擬總結上篇 。 一.虛擬機類加載機制 概述 虛擬機把描述類的數據從Clas...
摘要:與都繼承自類,在中也是使用字符數組保存字符串,,這兩種對象都是可變的。采用字節碼的好處語言通過字節碼的方式,在一定程度上解決了傳統解釋型語言執行效率低的問題,同時又保留了解釋型語言可移植的特點。 String和StringBuffer、StringBuilder的區別是什么?String為什么是不可變的? String和StringBuffer、StringBuilder的區別 可變性...
摘要:最近在備戰面試的過程中,整理一下面試題。成員變量如果沒有被賦初值,則會自動以類型的默認值而賦值一種情況例外被修飾但沒有被修飾的成員變量必須顯示地賦值而局部變量則不會自動賦值。 最近在備戰面試的過程中,整理一下面試題。大多數題目都是自己手敲的,網上也有很多這樣的總結。自己感覺總是很亂,所以花了很久把自己覺得重要的東西總結了一下。 面向對象和面向過程的區別 面向過程: 優點:性能比面...
摘要:編譯器只需面向,生成能理解的代碼或字節碼文件。源文件經編譯器,編譯成字節碼程序,通過將每一條指令翻譯成不同平臺機器碼,通過特定平臺運行。漲見識,字節碼執行過程分析。解決辦法減少默認棧的容量來換取更多的線程支持。 前言 JVM是java的核心和基礎,在java編譯器和os平臺之間的虛擬處理器。它是一種基于下層的操作系統和硬件平臺并利用軟件方法來實現的抽象的計算機,可以在上面執行java的...
閱讀 1117·2023-04-26 03:02
閱讀 1161·2023-04-25 19:18
閱讀 2583·2021-11-23 09:51
閱讀 2561·2021-11-11 16:55
閱讀 2614·2021-10-21 09:39
閱讀 1694·2021-10-09 09:59
閱讀 1991·2021-09-26 09:55
閱讀 3512·2021-09-26 09:55