一、JVM記憶體模型圖解 JVM 執行時期資料區 (JVM Runtime Area) 其實是指 JVM 在運作期間,其對JVM記憶體空間的分割與分配。網路上找到兩張圖如下圖所示(個人認為第二張圖Native Method Stack應該畫在Java Thead模組中): 二、各資料區域介紹 1、堆疊區 #堆疊分為java虛擬機器堆疊與本機方法堆疊 #重點是Java虛擬機棧,它是執行緒私有的,生命週期與執行緒相同。 每個方法執行都會建立一個堆疊幀,用於存放局部變數表,操作棧,動態鏈接,方法出口等。每個方法從被調用,直到被執行完。對應一個堆疊幀在虛擬機器中從入棧到出棧的過程。 通常說的堆疊是指局部變數表部分,存放編譯期間可知的8種基本資料類型,及物件參考和指令位址。局部變數表是在編譯期間完成分配,當進入一個方法時,這個堆疊中的局部變數分配記憶體大小是確定的。 會有兩種例外StackOverFlowError和 OutOfMemoneyError。當執行緒請求堆疊深度大於虛擬機器所允許的深度就會拋出StackOverFlowError錯誤;虛擬機棧動態擴展,當擴充功能無法申請到足夠的記憶體空間時候,拋出OutOfMemoneyError。 本地方法堆疊為虛擬機器使用到的本機方法服務(native),也是執行緒私有的。 2、堆區 #堆被所有執行緒共享區域,在虛擬機器啟動時創建,唯一目的存放物件實例。 堆區是gc的主要區域,通常情況下分為兩個區塊年輕代和年老代。更細一點年輕代又分為Eden區最要放新創建對象,From survivor 和 To survivor 保存gc後倖存下的對象,默認情況下各自佔比 8:1:1。 不過很多文章介紹分成3個區塊,把方法區算為永久代。這大概是基於Hotspot虛擬機器劃分, 然後例如IBM j9就不存在永久代概論。不管怎麼分區,都是存放物件實例。 會有異常OutOfMemoneyError #3、方法區 被所有執行緒共享區域,用於存放已被虛擬機載入的類別信息,常數,靜態變數等資料。被Java虛擬機器描述為堆疊的一個邏輯部分。習慣是也叫它永久代(permanment generation) #垃圾回收很少光顧這個區域,不過也是需要回收的,主要針對常量池回收,類型卸載。 常數池用於存放編譯期產生的各種字節碼和符號引用,常數池具有一定的動態性,裡面可以存放編譯期產生的常數;運行期間的常數也可以加入進入常數池中,例如string的intern()方法。 4、程式計數器 #目前執行緒所執行的行號指示器。透過改變計數器的值來決定下一指令,例如循環,分支,跳轉,異常處理,執行緒恢復等都是依賴計數器來完成。 Java虛擬機器多執行緒是透過執行緒輪流切換並分配處理器執行時間的方式實現的。 為了執行緒切換能恢復到正確的位置,每個執行緒都需要一個獨立的程式計數器,所以它是執行緒私有的。 唯一一塊Java虛擬機器沒有規定任何OutofMemoryError的區塊 三、資料區域總表 #名稱 」特徵######### ######作用##################設定參數##################異常##### ## 程式計數器 佔用記憶體小,執行緒私有,生命週期與執行緒相同 #大致為字節碼行號指示器 #無 #無 虛擬機器堆疊 線程私有,生命週期與執行緒相同,使用連續的記憶體空間 Java 方法執行的記憶體模型,儲存局部變數表、操作堆疊、動態連結、方法出口等資訊 -Xss StackOverflowError OutOfMemoryError java堆疊 ##線程共享,生命週期與虛擬機器相同,可以不使用連續的記憶體位址 儲存物件實例,所有物件實例(包括陣列)都要在堆上分配 #-Xms-Xsx-Xmn #OutOfMemoryError #方法區 執行緒共享,生命週期與虛擬機器相同,可以不使用連續的記憶體位址 儲存已被虛擬機器載入的類別資訊、常數、靜態變數、即時編譯器編譯後的程式碼等資料 -XX:PermSize:16M #-XX:MaxPermSize:64M OutOfMemoryError ##執行階段常數池方法區的一部分,具有動態性。存放字面量及符號參考 四、延伸:直接記憶體 直接記憶體(Direct Memory)並非虛擬機器執行時間資料區的一部分,也不是Java虛擬機器規範中定義的記憶體區域,但是這部分記憶體也被頻繁地使用,而且也可能導致OutOfMemoryError 異常出現,所以我們放到這裡一起講解。 在JDK 1.4 中新加入了NIO(NewInput/Output)類,引入了一種基於通道(Channel)與緩衝區(Buffer)的I/O 方式,它可以使用Native 函數庫直接分配堆外內存,然後透過一個儲存在Java 堆裡面的DirectByteBuffer 物件作為這塊內存的引用進行操作。這樣能在一些場景中顯著提高效能,因為避免了在Java 堆和Native 堆中來回複製資料。 五、延伸:堆疊與堆疊的比較 經常有人把Java 記憶體區分為堆疊記憶體(Heap)與堆疊記憶體(Stack),這種分法比較粗糙,Java記憶體區域的劃分其實遠比這複雜。這種劃分方式的流行只能說明大多數程式設計師最關注的、與物件記憶體分配關係最密切的記憶體區域是這兩塊。 堆很靈活,但是不安全。對於對象,我們要動態地創建、銷毀,不能說後創建的對象沒有銷毀,先前創建的對象就不能銷毀,那樣的話我們的程序就寸步難行,所以Java中用堆來存儲對象。而一旦堆中的物件被銷毀,我們繼續引用這個物件的話,就會出現著名的 NullPointerException,這就是堆的缺點——錯誤的引用邏輯只會在運行時才會被發現。 堆疊不靈活,但是很嚴格,是安全的,易於管理。因為只要上面的引用沒有銷毀,下面引用就一定還在,在大部分程式中,都是先定義的變數、引用先進棧,後定義的後進棧,同時,區塊內部的變數、引用在進入區塊時壓棧,區塊結束時出棧,理解了這個機制,我們就可以很方便地理解各種程式語言的作用域的概念了,同時這也是棧的優點——錯誤的引用邏輯在編譯時就可以被發現。 堆疊--主要存放參考和基本資料型別。 堆--用來存放 new 出來的物件實例。 參考: # #