JVM可謂是學習JAVA基礎中的基礎了,但仍有不少同學對JVM概念還是比較模糊,甚至沒有聽說過,對java的理解也只是在基礎語法 層面,本文就將對JVM進行初步介紹,因篇幅所限,只能介紹JVM基礎,如需要進一步學習,建議閱讀機械工業出版社出版的《深入理解JAVA虛擬機》。
請尊重作者勞動成果,轉載請標明原文鏈接:
Java虛擬機規范中規定的JVM如下圖所示:


可以看出,JVM由JVM運行時數據區(圖示中藍色框包含部分)、執行引擎、本地庫接口、本地方法庫組成。
JVM運行時數據區,分為線程共享部分(方法區、堆)和線程隔離區(虛擬機棧、本地方法棧和程序計數器)。
1.方法區
用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。
運行時常量池(Runtime Constant Pool)是方法區的一部分。.Class文件中除了有類的版本/字段/方法/接口等描述信息外,還有一項信息是常量池(Constant Pool Table),用於存放編譯期生成的各種字面量和符號引用,這部分內容將類在加載后進入方法區的運行時常量池中存放.
運行時常量區的內容並不只是在編譯期間產生,通過String.intern()也可以實現在運行時向常量區中添加內容。
需要注意的是:從JDK8開始,方法區被元數據區替代了。具體的原因和兩者的區別可以參考官網。
2.堆
是JVM中最大的一塊內存區域,該區域的目的只是用於存儲對象實例及數組。該區域也是GC的最主要區域。
根據Java虛擬機規范的規定,Java堆可以處於物理上不連續的內存空間中,只要邏輯上是連續的即可,就像我們的磁盤空間一樣.在實現時,既可以實現固定大小的,也可以是可擴展的,不過當前主流的虛擬機都是按照可擴展來實現的(通過-Xmx和-Xms控制).如果在堆中沒有內存完成實例分配,並且堆也無法再擴展時,就會拋出OutOfMemoryError異常。
3.虛擬機棧
每個線程方法在執行時都會創建一個棧幀,包含局部變量表、返回地址、操作數棧等信息。每個方法的執行與完成就對應的棧幀的入棧與出棧過程 。局部變量表占用空間的大小在編譯期就確定了。這里需要注意:如果線程請求的棧深度大於虛擬機所允許的深度,將會拋出StackOverflowError異常;如果虛擬機棧可以動態擴展(當前大部分的Java虛擬機都可動態擴展,只不過Java虛擬機規范中也允許固定長度的虛擬機棧),當擴展時無法申請到足夠的內存時將會拋出OutOfMemoryError異常。
4.本地方法棧
與虛擬機棧類似,不過其中執行是本地方法。對於HotSpot虛擬機而言,本地方法棧和虛擬機棧是統一的。
5.程序計數器
是一個小的內存空間,如果線程正在執行的是一個java方法,則此內存區域記錄正在執行的虛擬機字節碼指令的地址;如果線程正在執行的是native方法,則計算器中的值為空。此內存區域是唯一一個在JAVA虛擬機規范中沒有規定任何OutOfMemoryError情況的區域。
這幾部分都有相關的JDK自帶工具可以分析查看,比如jps, jstack, jmap, jhat, jstat等,還有圖形化工具jconsole,jvisualvm,但對於Linux服務器就無能為力了。
好了,關於JVM就講完了,接下來進行提問:JVM哪些區域會發生OOME,以及各自原因是什么?
堆內存不足是最常見的 OOM 原因之一,拋出的錯誤信息是“java.lang.OutOfMemoryError:Java heap space”,原因可能千奇百怪,例如,可能存在內存泄漏問題;也很有可能就是堆的大小不合理,比如我們要處理比較可觀的數據量,但是沒有顯式指定 JVM 堆大小或者指定數值偏小;或者出現 JVM 處理引用不及時,導致堆積起來,內存無法釋放等。
而對於 Java 虛擬機棧和本地方法棧,這里要稍微復雜一點。如果我們寫一段程序不斷的進行遞歸調用,而且沒有退出條件,就會導致不斷地進行壓棧。類似這種情況,JVM 實際會拋出 StackOverFlowError;當然,如果 JVM 試圖去擴展棧空間的的時候失敗,則會拋出 OutOfMemoryError。
對於老版本的 Oracle JDK,因為永久代的大小是有限的,並且 JVM 對永久代垃圾回收(如,常量池回收、卸載不再需要的類型)非常不積極,所以當我們不斷添加新類型的時候,永久代出現 OutOfMemoryError 也非常多見,尤其是在運行時存在大量動態類型生成的場合;類似 Intern 字符串緩存占用太多空間,也會導致 OOM 問題。對應的異常信息,會標記出來和永久代相關:“java.lang.OutOfMemoryError: PermGen space”。
隨着元數據區的引入,方法區內存已經不再那么窘迫,所以相應的 OOM 有所改觀,出現 OOM,異常信息則變成了:“java.lang.OutOfMemoryError: Metaspace”。
直接內存不足,也會導致 OOM。
后記:
JVM是JAVA面試過程中最容易被問到的基礎題目,在本篇JVM介紹完后,后面還准備了更多有意思的JAVA基礎面試題,這些將作為提升JAVA基礎能力的免費系列課程,從面試題出發,對每個問題舉一反三,以點帶面,不做不求甚解之人,最終目標不是學會答題,而是學會題目本身涉及到的所有知識點。暫定的全部面試題目課程包含如下內容:
談談你對JVM的理解?
final,finally,finalize有什么區別?
String,StringBuffer,StringBuilder有什么區別?
Exception和Error有什么區別?
Hashtable,HashMap,TreeMap有什么區別?
Vector,ArrayList,LinkedList有什么區別?
int和Integer有什么區別?
接口和抽象類有什么區別?
如何保證集合是線程安全的?
談談你知道的設計模式?
搜索關注微信公眾號“程序員姜小白”,獲取更新精彩內容哦。