談談對JVM的理解


        JVM可謂是學習JAVA基礎中的基礎了,但仍有不少同學對JVM概念還是比較模糊,甚至沒有聽說過,對java的理解也只是在基礎語法 層面,本文就將對JVM進行初步介紹,因篇幅所限,只能介紹JVM基礎,如需要進一步學習,建議閱讀機械工業出版社出版的《深入理解JAVA虛擬機》。

        請尊重作者勞動成果,轉載請標明原文鏈接:

   https://www.cnblogs.com/jpcflyer/p/10666099.html

        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有什么區別?
        接口和抽象類有什么區別?
        如何保證集合是線程安全的?
        談談你知道的設計模式?
 
        搜索關注微信公眾號“程序員姜小白”,獲取更新精彩內容哦。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM