Java虛擬機在執行Java程序的過程時,會將它管理的內存劃分為若干個不同的數據區域。主要分為以下幾個區域:
程序計數器
當前線程所執行的字節碼的行號指示器。字節碼解釋器工作時通過改變程序計數器來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理等基礎功能都需要依賴程序計數器。
如果線程正在執行的是一個Java 方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是Natvie 方法,這個計數器值則為空。此區域不會出現OutOfMemoryError異常。
虛擬機棧
線程私有。每個方法在執行的同時會創建一個棧幀用于存儲局部變量表、操作棧、動態鏈接、方法出口等信息。每一個方法被調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。
Java虛擬機規范中,對這個區域規定了兩種異常狀況:如果線程請求的棧深度大于虛擬機所允許的深度,將拋出StackOverflowError異常;如果虛擬機棧可以動態擴展,當擴展時無法申請到足夠的內存時會拋出OutOfMemoryError異常。
本地方法棧
虛擬機棧為虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則是為虛擬機使用到的Native方法服務。與虛擬機棧一樣,本地方法棧區域也會拋出StackOverflowError和OutOfMemoryError異常。
Java堆
Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建。此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內存。根據Java虛擬機規范的規定,Java堆可以處于物理上不連續的內存空間中,只要邏輯上是連續的即可,如果在堆中沒有內存完成實例分配,并且堆也無法再擴展時,將會拋出OutOfMemoryError異常。
方法區
方法區與Java堆一樣,是各個線程共享的內存區域,它用于存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。Java虛擬機規范對這個區域的限制非常寬松,除了和Java堆一樣不需要連續的內存和可以選擇固定大小或者可擴展外,還可以選擇不實現垃圾收集。這個區域的內存回收目標主要是針對常量池的回收和對類型的卸載。根據Java虛擬機規范的規定,當方法區無法滿足內存分配需求時,將拋出OutOfMemoryError異常。
運行時常量池
運行時常量池是方法區的一部分。Class文件中除了有類的版本、字段、方法、接口等描述等信息外,還有一項信息是常量池,用于存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載后進入方法區的運行時常量池中存放。運行時常量池是方法區的一部分,自然會受到方法區內存的限制,當常量池無法再申請到內存時會拋出OutOfMemoryError異常。
直接內存
直接內存不是虛擬機運行時數據區的一部分,也不是Java虛擬機規范中定義的內存區域。這部分內存也被頻繁使用,也會出現OutOfMemoryError異常。在JDK1.4中新加入NIO類,引入了一種基于通道與緩沖區的I/O方式,它可以使用Native函數庫直接分配堆外內存,然后通過一個存儲在Java堆中的DIrectByteBuffer對象作為這塊內存的引用進行操作。這在一定場景中避免了Java堆和Native堆中來回復制數據。
來源:《深入理解Java虛擬機》