JAVA內存區域(1.7及之前)

Java虛擬機在執行Java程序是會將內存劃分爲不同區域,不同的區域負責不同的工作。

如下圖:

 

圖中藍色的數據區爲線程共享數據區,白色的是線程私有數據區。

程序計數器

程序計數器是一塊較小的內存空間,可以看成是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏,字節碼解釋器改變這個計數器的值來選擇下一條要執行的字節碼指令,程序的判斷、循環、跳轉所有功能都是不同行的字節碼,控制這個行數就控制了程序的運行流程,就知道下一步該執行什麼,因爲只保存行數,所以這塊區域不大,同樣的,爲了保證每個線程的獨立運行,所以每個線程都有自己的程序計數器,不同線程互不影響。

Java虛擬機棧

java虛擬機棧存儲的是方法內部的變量,每個方法執行的時候會創建一個棧幀用來存儲方法內的局部變量表等信息,每個方法開始執行的時候創建棧幀併入棧,方法執行完棧幀出棧。虛擬機棧也是線程私有的,生命週期與線程的生命週期一致,用來存儲一個線程執行過程中方法內部的局部變量表等信息。

局部變量表中存放了編譯期可知的各種基本數據類型(boolean,byte,short,int ,long,char,float,double)和對象引用(reference)和方法返回(returnAddress)類型。

其中64位長度的long和double類型數據會佔用2個局部變量空間,其餘的類型只佔一個。局部變量表的內存空間在編譯期就完成分配,當進入一個方法時需要在棧幀中分配多大的局部變量空間是完全確定的。

如果線程請求的棧深度大於虛擬機規定的深度,將拋出StackOverflowError。如果虛擬機棧可以動態擴展,在擴展時如果無法申請到足夠的內存,則會拋出OutOfMemoryError異常。

本地方法棧

本地方法棧的作用和虛擬機棧的作用非常類似,只不過虛擬機棧是爲java方法服務,而本地方法棧則是爲本地Native方法服務。本地方法棧也會拋出StackOverflowError和OutOfMemoryError異常。

Java堆

對於大多數應用來說,Java堆是Java虛擬機所管理的內存中最大的一塊。堆是所有線程共享的一塊內存區域,在虛擬機啓動時創建。堆是用來存放對象實例的。(隨着JIT編譯期的發展和逃逸分析技術的成熟,所有對象都在堆中漸漸變的不那麼“絕對”了)。

根據Java虛擬機規範,堆可以處於物理上不連續的內存空間中。堆既可以是固定大小的,也可以是動態擴展的,目前主流虛擬機都是按照可擴展實現的(通過-Xmx和-Xms控制)。如果在堆中沒有足夠空間分配實例,也無法再擴展時,將會拋出OutOfMemoryError異常。

堆雖然都是存放對象的,但是在不同角度看來,仍然是有不同的區域劃分的,從內存回收的角度看,可以分爲新生代和老年代,在細緻一點有Eden,From Survior,To Sruvivor空間。從內存分配的角度看,雖然堆是線程共享的,但是仍然有線程私有的分配緩衝區。但這都是根據業務功能的劃分,不管如何劃分,不論哪個區域,堆中存儲的都是對象。

方法區

方法區和堆一樣,也是線程共享的內存空間,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。目前發佈的JDK1.7中,已經把原本放在方法區的字符串常量池移除(JDK1.6字符串常量池在方法區,1.7移到了堆中,1.8用元空間代替了方法區)。

方法區出了和堆一樣不需要連續的內存空間和可以選擇固定大小或者支持擴展外,還可以不實現垃圾收集。方法區的垃圾收集主要是針對常量池的回收和對類型的卸載,這個區域的垃圾回收成績難以令人滿意,尤其是類型的卸載,條件相當苛刻。(android系統的java虛擬機方法區的垃圾回收如何)。方法區無法滿足內存分配需求時,將拋出OutOfMemoryError異常。

方法區中有一部分叫運行時常量池,用於存放編譯期生成的各種字面量和符號引用,暫不關注。

直接內存

直接內存並不是虛擬機運行時的數據區的一部分。

JDK1.4中新加入了NIO類,引入了一種基於通道和緩衝區的I/O方式,可以使用Native函數庫直接分配堆外內存,然後通過一個Java堆中的DirectByteBuffer對象作爲這塊內存的引用進行操作,這樣避免了在Java堆和Native堆中來回複製數據,顯著提高性能。

雖然該處內存不受Java堆大小顯示,但是肯定是收到硬件限制的,因此當使用過多,過渡佔用硬件內存,也會導致堆無法擴展而出現OutOfMemoryError。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章