JVM入門之JVM內(nèi)存結(jié)構(gòu)內(nèi)容詳解
一、java代碼編譯執(zhí)行過程
- 源碼編譯:通過Java源碼編譯器將Java代碼編譯成JVM字節(jié)碼(.class文件)
- 類加載:通過ClassLoader及其子類來完成JVM的類加載
- 類執(zhí)行:字節(jié)碼被裝入內(nèi)存,進入JVM虛擬機,被解釋器解釋執(zhí)行
注:Java平臺由Java虛擬機和Java應用程序接口搭建,Java語言則是進入這個平臺的通道,
用Java語言編寫并編譯的程序可以運行在這個平臺上
二、JVM簡介
1.java程序經(jīng)過一次編譯之后,將java代碼編譯為字節(jié)碼也就是class文件,然后在不同的操作系統(tǒng)上依靠不同的java虛擬機進行解釋,最后再轉(zhuǎn)換為不同平臺的機器碼,最終得到執(zhí)行
2.Java虛擬機(JVM) 處在核心的位置,是程序與底層操作系統(tǒng)、硬件無關(guān)的關(guān)鍵。
JVM的下方是移植接口,移植接口由兩部分組成:適配器和Java操作系統(tǒng), 其中依賴于平臺的部分稱為適配器,JVM 通過移植接口在具體的平臺和操作系統(tǒng)上實現(xiàn)
JVM 的上方是Java的基本類庫和擴展類庫以及它們的API, 利用Java API編寫的應用程序(application) 和小程序(Java applet) 可以在任何Java平臺上運行而無需考慮底層平臺
Java虛擬機(JVM)實現(xiàn)了程序與操作系統(tǒng)的分離,從而實現(xiàn)了Java 的跨平臺
3.JVM在它的生存周期中有一個明確的任務,那就是運行Java程序,因此當Java程序啟動的時候,就產(chǎn)生JVM的一個實例;當程序運行結(jié)束的時候,該實例也跟著消失了
4.三種JVM:① Sun公司的HotSpot ② BEA公司的JRockit ③ IBM公司的J9 JVM
在JDK1.7及其以前我們所使用的都是Sun公司的HotSpot,但由于Sun公司和BEA公司都被oracle收購,jdk1.8將采用Sun公司的HotSpot和BEA公司的JRockit兩個JVM中精華形成jdk1.8的JVM。
三、JVM體系結(jié)構(gòu)
四、JVM內(nèi)存結(jié)構(gòu)
1.程序計數(shù)器 PC Register
每個線程都有一個程序計算器,就是一個指針,指向方法區(qū)中的方法字節(jié)碼(下一個將要執(zhí)行的指令代碼),由執(zhí)行引擎讀取下一條指令,是一個非常小的內(nèi)存空間,幾乎可以忽略不記。
程序計數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,它的作用可以看做是當前線程所執(zhí)行的字節(jié)碼的行號指示器。在虛擬機的概念模型里(僅是概念模型,各種虛擬機可能會通過一些更高效的方式去實現(xiàn)),字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復等基礎(chǔ)功能都需要依賴這個計數(shù)器來完成。由于Java 虛擬機的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實現(xiàn)的,在任何一個確定的時刻,一個處理器(對于多核處理器來說是一個內(nèi)核)只會執(zhí)行一條線程中的指令。因此,為了線程切換后能恢復到正確的執(zhí)行位置,每條線程都需要有一個獨立的程序計數(shù)器,各條線程之間的計數(shù)器互不影響,獨立存儲,我們稱這類內(nèi)存區(qū)域為“線程私有”的內(nèi)存。如果線程正在執(zhí)行的是一個Java 方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址;如果正在執(zhí)行的是Natvie 方法,這個計數(shù)器值則為空(Undefined)。此內(nèi)存區(qū)域是唯一一個在Java 虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError 情況的區(qū)域。
2.本地方法棧 Native Method Stack
Native Method Stack中登記native方法,在Execution Engine執(zhí)行時加載native libraies
本地方法棧與虛擬機棧基本類似,區(qū)別在于虛擬機棧為虛擬機執(zhí)行的java方法服務,而本地方法棧則是為Native方法服務
3.方法區(qū) Method Area
用于存儲虛擬機加載的:靜態(tài)變量+常量+類信息+運行時常量池 (類信息:類的版本、字段、方法、接口、構(gòu)造函數(shù)等描述信息 )
默認最小值為16MB,最大值為64MB,可以通過-XX:PermSize 和 -XX:MaxPermSize 參數(shù)限制方法區(qū)的大小
對于習慣在HotSpot 虛擬機上開發(fā)和部署程序的開發(fā)者來說,很多人愿意把方法區(qū)稱為“永久代”(Permanent Generation),本質(zhì)上兩者并不等價,僅僅是因為HotSpot 虛擬機的設(shè)計團隊選擇把GC 分代收集擴展至方法區(qū),或者說使用永久代來實現(xiàn)方法區(qū)而已。對于其他虛擬機(如BEA JRockit、IBM J9 等)來說是不存在永久代的概念的。即使是HotSpot 虛擬機本身,根據(jù)官方發(fā)布的路線圖信息,現(xiàn)在也有放棄永久代并“搬家”至Native Memory 來實現(xiàn)方法區(qū)的規(guī)劃了。Java 虛擬機規(guī)范對這個區(qū)域的限制非常寬松,除了和Java 堆一樣不需要連續(xù)的內(nèi)存和可以選擇固定大小或者可擴展外,還可以選擇不實現(xiàn)垃圾收集。相對而言,垃圾收集行為在這個區(qū)域是比較少出現(xiàn)的,但并非數(shù)據(jù)進入了方法區(qū)就如永久代的名字一樣“永久”存在了。這個區(qū)域的內(nèi)存回收目標主要是針對常量池的回收和對類型的卸載,一般來說這個區(qū)域的回收“成績”比較難以令人滿意,尤其是類型的卸載,條件相當苛刻,但是這部分區(qū)域的回收確實是有必要的。在Sun 公司的BUG 列表中,曾出現(xiàn)過的若干個嚴重的BUG 就是由于低版本的HotSpot 虛擬機對此區(qū)域未完全回收而導致內(nèi)存泄漏。根據(jù)Java 虛擬機規(guī)范的規(guī)定,當方法區(qū)無法滿足內(nèi)存分配需求時,將拋出OutOfMemoryError 異常。
4.棧 JVM Stack
編譯器可知的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對象引用(引用指針,并非對象本身)
棧是java 方法執(zhí)行的內(nèi)存模型:
每個方法被執(zhí)行的時候 都會創(chuàng)建一個“棧幀”用于存儲局部變量表(包括參數(shù))、操作棧、方法出口等信息。
每個方法被調(diào)用到執(zhí)行完的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。
?。ň植孔兞勘恚捍娣帕司幾g器可知的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對象引用(引用指針,并非對象本身),
其中64位長度的long和double類型的數(shù)據(jù)會占用2個局部變量的空間,其余數(shù)據(jù)類型只占1個。
局部變量表所需的內(nèi)存空間在編譯期間完成分配,當進入一個方法時,這個方法需要在棧幀中分配多大的局部變量是完全確定的,在運行期間棧幀不會改變局部變量表的大小空間)
棧的生命期是跟隨線程的生命期,線程創(chuàng)建時創(chuàng)建,線程結(jié)束棧內(nèi)存也就釋放,是線程私有的。
5.堆 Java Heap
所有的對象實例以及數(shù)組都要在堆上分配,此內(nèi)存區(qū)域的唯一目的就是存放對象實例
堆是Java 虛擬機所管理的內(nèi)存中最大的一塊。Java 堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機啟動時創(chuàng)建
堆是理解Java GC機制最重要的區(qū)域,沒有之一
結(jié)構(gòu):新生代(Eden區(qū)+2個Survivor區(qū)) 老年代 永久代(HotSpot有)
新生代:新創(chuàng)建的對象——>Eden區(qū)
GC之后,存活的對象由Eden區(qū) Survivor區(qū)0進入Survivor區(qū)1
再次GC,存活的對象由Eden區(qū) Survivor區(qū)1進入Survivor區(qū)0
老年代:對象如果在新生代存活了足夠長的時間而沒有被清理掉(即在幾次Young GC后存活了下來),則會被復制到老年代
如果新創(chuàng)建對象比較大(比如長字符串或大數(shù)組),新生代空間不足,則大對象會直接分配到老年代上(大對象可能觸發(fā)提前GC,應少用,更應避免使用短命的大對象)
老年代的空間一般比新生代大,能存放更多的對象,在老年代上發(fā)生的GC次數(shù)也比年輕代少
永久代:可以簡單理解為方法區(qū)(本質(zhì)上兩者并不等價)
如上文所說:對于習慣在HotSpot 虛擬機上開發(fā)和部署程序的開發(fā)者來說,很多人愿意把方法區(qū)稱為“永久代”,本質(zhì)上兩者并不等價
僅僅是因為HotSpot 虛擬機的設(shè)計團隊選擇把GC 分代收集擴展至方法區(qū),或者說使用永久代來實現(xiàn)方法區(qū)而已
對于其他虛擬機(如BEA JRockit、IBM J9 等)來說是不存在永久代的概念的
即使是HotSpot 虛擬機本身,根據(jù)官方發(fā)布的路線圖信息,現(xiàn)在也有放棄永久代并“搬家”至Native Memory 來實現(xiàn)方法區(qū)的規(guī)劃了
Jdk1.6及之前:常量池分配在永久代
Jdk1.7:有,但已經(jīng)逐步“去永久代”
Jdk1.8及之后:沒有永久代(java.lang.OutOfMemoryError: PermGen space,這種錯誤將不會出現(xiàn)在JDK1.8中)
6.直接內(nèi)存 Direct Memor
直接內(nèi)存并不是JVM管理的內(nèi)存,可以這樣理解,直接內(nèi)存,就是JVM以外的機器內(nèi)存,比如,你有4G的內(nèi)存,JVM占用了1G,則其余的3G就是直接內(nèi)存
JDK中有一種基于通道(Channel)和緩沖區(qū)(Buffer)的內(nèi)存分配方式,將由C語言實現(xiàn)的native函數(shù)庫分配在直接內(nèi)存中,用存儲在JVM堆中的DirectByteBuffer來引用
由于直接內(nèi)存收到本機器內(nèi)存的限制,所以也可能出現(xiàn)OutOfMemoryError的異常。
參考資料
1.JVM工作原理
到此這篇關(guān)于JVM入門之JVM內(nèi)存結(jié)構(gòu)內(nèi)容詳解的文章就介紹到這了,更多相關(guān)JVM入門之JVM內(nèi)存結(jié)構(gòu)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot如何設(shè)置404、500返回統(tǒng)一格式json
這篇文章主要介紹了SpringBoot如何設(shè)置404、500返回統(tǒng)一格式json問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08SpringBoot整合MyBatis逆向工程及 MyBatis通用Mapper實例詳解
這篇文章主要介紹了SpringBoot整合MyBatis逆向工程及 MyBatis通用Mapper實例詳解 ,需要的朋友可以參考下2017-09-09Java面向?qū)ο蟪绦蛟O(shè)計:繼承,多態(tài)用法實例分析
這篇文章主要介紹了Java面向?qū)ο蟪绦蛟O(shè)計:繼承,多態(tài)用法,結(jié)合實例形式分析了java繼承與多態(tài)的相關(guān)概念、原理、實現(xiàn)方法與操作注意事項,需要的朋友可以參考下2020-04-04SpringBoot+MinIO實現(xiàn)對象存儲方式
這篇文章主要介紹了SpringBoot+MinIO實現(xiàn)對象存儲方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08Java高效提取PDF文件指定坐標的文本內(nèi)容實戰(zhàn)代碼
在日常工作中,有時可能會需要從龐大的PDF文檔中提取其中所包含的文本內(nèi)容,下面這篇文章主要給大家介紹了關(guān)于如何利用Java高效提取PDF文件指定坐標的文本內(nèi)容,需要的朋友可以參考下2024-01-01