面試官:請介紹一下JVM內存結構


Java SE體系架構


面試官:請介紹一下JVM內存結構

Java SE(Java Platform,Standard Edition),Java 平臺標準版,以前稱為 J2SE。它允許開發和部署在桌面、服務器、嵌入式環境和實時環境中使用的 Java 應用程序。Java SE 包含了支持 Java Web 服務開發的類,併為 Java Platform,Enterprise Edition(Java EE)提供基礎。

Java EE(Java Platform,Enterprise Edition)企業版本幫助開發和部署可移植、健壯、可伸縮且安全的服務器端 Java 應用程序。

Java ME(Java Platform,Micro Edition)為在移動設備和嵌入式設備(比如手機、PDA、電視機頂盒和打印機)上運行的應用程序提供一個健壯且靈活的環境。

JDK:Java 開發工具包,JDK 是 JRE 的超集,包含 JRE 中的所有內容,以及開發程序所需的編譯器和調試程序等工具。

JRE:Java SE 運行時環境 ,提供庫、Java 虛擬機和其他組件來運行用 Java 編程語言編寫的程序。主要類庫,包括:程序部署發佈、用

戶界面工具類、繼承庫、其他基礎庫,語言和工具基礎庫

JVM:java 虛擬機,負責 JavaSE 平臺的硬件和操作系統無關性、編譯執行代碼(字節碼)和平臺安全性

虛擬機發展史

解釋執行和編譯執行(針對字節碼的執行)

解釋執行就是邊翻譯為機器碼邊執行、即時編譯(編譯執行)就是先將一個方法中的所有字節碼全部編譯成機器碼之後再執行。

Hotspot 採用的是先解釋執行,到了一定時機後熱點代碼(多次執行、循環等)再翻譯成機器碼

熱點代碼探測技術(通過執行計數器找到最有編譯價值的代碼,如果代碼用得非常頻繁,就會把這些代碼編譯成本地代碼)。

JRockit 採取的方法是在執行 class 時直接編譯為機器碼(Java 程序啟動速度會比較慢)

J9 和 Hotspot 比較接近,主要是用在 IBM 產品(IBM WebSphere 和 IBM 的 AIX 平臺上),華為有的項目用的 J9。

谷歌:Google Android Dalivk VM:使用的寄存器架構,執行 dex(Dalvik Executable)通過 class 轉化而來。

JVM 的整體介紹


面試官:請介紹一下JVM內存結構

首先class文件進行類加載,加載到JVM的運行時數據區然後執行引擎執行對應的機器碼。

學習JVM的時候需要重點關注JVM的內存分配、垃圾回收。

JVM內存結構


面試官:請介紹一下JVM內存結構

運行時數據區域:


面試官:請介紹一下JVM內存結構

JVM在運行過程中會把它所管理的內存劃分成若干不同的數據區域!

線程私有:程序計數器、虛擬機棧、本地方法棧

線程共享:堆、方法區


計算機的運行=指令+數據,指令用於執行方法的,數據用於存放數據和對象的。

虛擬機棧----執行 java 方法、

本地方法棧---執行本地方法、

程序計數器---程序執行的計數器

Java 中的數據:變量、常量、對象、數組相關。

程序計數器

較小的內存空間,當前線程執行的字節碼的行號指示器;各線程之間獨立存儲,互不影響。

如果線程正在執行的是一個 Java 方法,則指明當前線程執行的代字節碼行數。

如果正在執行的是 Natvie 方法,這個計數器值則為空(Undefined)。

此內存區域是唯一一個不會出現 OutOfMemoryError 情況的區域。

本地方法棧

本地方法棧保存的是native方法的信息,當一個JVM創建的線程調用native方法後,JVM不再為其在虛擬機棧中創建棧幀,JVM只是簡單地動態鏈接並直接調用native方法。

各虛擬機自由實現,本地方法棧 native 方法調用 JNI 到了底層的 C/C++(c/c++可以觸發彙編語言,然後驅動硬件)

虛擬機棧

每個線程私有的,線程在運行時,在執行每個方法的時候都會打包成一個棧幀,存儲了局部變量表,操作數棧,動態鏈接,方法出口等信息,然後放入棧。每個時刻正在執行的當前方法就是虛擬機棧頂的棧楨。方法的執行就對應著棧幀在虛擬機棧中入棧和出棧的過程。

棧的大小缺省為 1M,可用參數 –Xss 調整大小,例如-Xss256k

在編譯程序代碼的時候,棧幀中需要多大的局部變量表,多深的操作數棧都已經完全確定了,並且寫入到方法表的 Code 屬性之中,因此一個棧幀需要分配多少內存,不會受到程序運行期變量數據的影響,而僅僅取決於具體的虛擬機實現。

局部變量表:顧名思義就是局部變量的表,用於存放我們的局部變量的。首先它是一個 32 位的長度,主要存放我們的 Java 的八大基礎數據類型,一般 32位就可以存放下,如果是 64 位的就使用高低位佔用兩個也可以存放下,如果是局部的一些對象,比如我們的 Object 對象,我們只需要存放它的一個引用地址即可。(基本數據類型、對象引用、returnAddress 類型)

操作數據棧:存放我們方法執行的操作數的,它就是一個棧,先進後出的棧結構,操作數棧,就是用來操作的,操作的的元素可以是任意的 java 數據類型,所以我們知道一個方法剛剛開始的時候,這個方法的操作數棧就是空的,操作數棧運行方法是會一直運行入棧/出棧的操作

動態連接: Java 語言特性多態(需要類加載、運行時才能確定具體的方法,後續有詳細的講解)

返回地址: 正常返回(調用程序計數器中的地址作為返回)

三步曲:恢復上層方法的局部變量表和操作數棧、把返回值(如果有的話)壓入調用者棧幀的操作數棧中、調整 PC 計數器的值以指向方法調用指令後面的一條指令、異常的話(通過異常處理器表來確定)

方法區(永久代(JDK1.7和以前)、元空間(JDK1.8))

方法區包含:類信息、常量、靜態變量、即時編譯期編譯後的代碼

類信息:類的完整有效名、返回值類型、修飾符(public,private...)、變量名、方法名、方法代碼、這個類型直接父類的完整有效名(除非這個類型是 interface 或是 java.lang.Object,兩種情況下都沒有父類)、類的直接接口的一個有序列表。

幾乎所有對象都分配在這裡,也是垃圾回收發生的主要區域,可用以下參數調整:

-Xms:堆的最小值;

-Xmx:堆的最大值;

-Xmn:新生代的大小;

-XX:NewSize;新生代最小值;

-XX:MaxNewSize:新生代最大值;

例如- Xmx256m

直接內存

使用 Native 函數庫直接分配堆外內存(NIO)

並不是 JVM 運行時數據區域的一部分,但是會被頻繁使用(可以通過-XX:MaxDirectMemorySize 來設置(默認與堆內存最大值一樣,也會出現 OOM 異常)避免了在 Java 堆和 Native 堆中來回複製數據,能夠提高效率測試用例 JavaStack:設置 JVM 參數-Xmx100m,運行異常,因為如果沒設置-XX:MaxDirectMemorySize,則默認與-Xmx 參數值相同,分配 128M 直接內存超出限制範圍

深入辨析堆和棧

功能

 以棧幀的方式存儲方法調用的過程,並存儲方法調用過程中基本數據類型的變量(int、short、long、byte、float、double、boolean、char 等)以及對象的引用變量,其內存分配在棧上,變量出了作用域就會自動釋放;

 而堆內存用來存儲 Java 中的對象。無論是成員變量,局部變量,還是類變量,它們指向的對象都存儲在堆內存中;

 線程獨享還是共享

 棧內存歸屬於單個線程,每個線程都會有一個棧內存,其存儲的變量只能在其所屬線程中可見,即棧內存可以理解成線程的私有內存。

 堆內存中的對象對所有線程可見。堆內存中的對象可以被所有線程訪問。

 空間大小

棧的內存要遠遠小於堆內存

棧溢出

參數:-Xss256k

java.lang.StackOverflowError 一般的方法調用是很難出現的,如果出現了要考慮是否有無限遞歸。

虛擬機棧溢出帶給我們的啟示:方法的執行因為要打包成棧楨,所以天生要比實現同樣功能的循環慢,所以樹的遍歷算法中:遞歸和非遞歸(循環來實現)都有存在的意義。遞歸代碼簡潔,非遞歸代碼複雜但是速度較快。OutOfMemoryError:不斷建立線程。(一般演示不出,演示出來機器也死了)

建議:能消除遞歸儘量消除遞歸。


以上介紹了java虛擬機的一些入門的基礎知識。如果對大家有用記得關注轉發一下。


分享到:


相關文章: