概述
對於從事C,C++程序員來說,程序員負責對內存的管理,擁有每個所創建對象的所有權,擔負着每個對象從出生到終結的維護責任。
但是,對於Java程序員來說,Java虛擬機擔負起了,對內存管理的責任。如果不瞭解虛擬機是怎麼樣使用內存的,那麼排查錯誤將會成爲一項異常艱難的工作了。
Java內存區域
引用自—https://segmentfault.com/a/1190000006051731
引用自—http://www.cnblogs.com/zhouyuqin/p/5161677.html
程序計數器(線程私有)
是一塊較小的內存空間,可以看作是當前線程所執行的字節碼的行號指示器。字節碼解釋器工作原理就是通過改變計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴計數器來完成。
Java虛擬機的多線程是通過線程輪流切換並分配處理器執行時間的方式來實現的,在任何一個確定的時刻,一個處理器(對於多核處理器來說指一個內核)都只會執行一條線程中的指令,爲了線程切換後恢復到正確的執行位置,每條線程都需要有一個獨立的程序計數器。所以這塊內存是“線程私有”的內存。
線程正在執行Java方法,計數器記錄正在執行的虛擬機字節碼指令的地址 ; 如果執行Native方法,則計數器值爲空(Undefined)。
該區域是虛擬機規範中唯一個沒有規定任何OutOfMemoryError情況的區域。
Java 虛擬機棧(線程私有)
生命週期與線程相同。描述的是Java方法執行的內存模型:每一個方法執行的同時都會創建一個棧幀(Starck Frame)用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。
每個方法從開始調用到執行完成的過程就對應着一個棧幀在虛擬機棧中入棧到出棧的過程。
局部變量表中存放了編譯期可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)以及對象引用(reference類型,它不是對象本身,可能是一個指向:對象起始地址的引用指針、對象句柄)和returnAddress類型(指向一條字節碼指令的地址)。
虛擬機規範中對這塊區域規定了兩種異常狀況:
1> 如果線程請求的棧深度大於虛擬機所允許的深度將拋出StackOverflowError異常
2> 如果虛擬機棧可以動態擴展,而擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError異常本地方法棧(線程私有)
與虛擬機棧的作用非常相似,區別就是:虛擬機棧爲虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧爲虛擬機使用到的Native方法服務。SunHotSpot虛擬機把虛擬機棧和本地方法棧合併了。與虛擬機棧一樣本地方法棧也會拋出StackOverflowError和OutOfMemoryError異常
Java堆(虛擬機啓動時創建,線程共享)
大多數應用中,堆被認爲是虛擬機內存中最大的一塊。所有對象實例以及數組都分配在堆上, 但是隨着Jit技術和逃逸分析,棧上分配,標量替換等技術的發展,界限也變得不是那麼絕對。堆是垃圾收集器管理的主要區域(也叫作GC堆)。從內存回收的角度看,收集器基本都採用分代收集算法,所以Java堆還可以細分爲:新生代和老年代,再細緻一點:Eden空間、From Survivor空間、To Survivor空間。從內存分配角度來看,線程共享的Java堆中,還可以劃分出多個線程私有的分配緩衝區(Thread Local Allocation Buffer, TLAB)。無論哪個角度都是爲了更好更快的回收和分配內存。
Java堆的實現可以使固定的大小,也可以是可擴展的,主流一般是通過可擴展的方式實現(通過-Xmx和-Xms控制)
如果Heap中沒有足夠的內存完成實例的分配,並且也無法再擴展時將會拋出OutOfMemoryError異常
方法區(線程共享 Non-Heap(非堆))
對於HotSpot虛擬機上開發者也把這塊區域成爲“永久代”(Permanent Generation)。兩者本質上並不等價,只是爲了垃圾收集器可以像管理 Java 堆一樣去管理這部分區域而把GC分代收集擴展到了這裏。用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯(Just In Time,JIT編譯器)後的代碼等數據(JDK 1.7 中字符串常量池已被移出)。
可以通過-XX:PermSize 和 -XX:MaxPermSize來設置這塊區域的可擴展性
當方法區不能滿足內存分配需求時,會拋出OutOfMemoryErrory運行時常量池(屬於方法區)
用於存放編譯期生成的各種字面量和符號引用,這部分內容在類加載後存入方法區的運行時常量池中(動態性)。當常量池無法申請到內存時會拋出OutOfMemoryErrory。
直接內存(既不屬於虛擬機運行時內數據區,也不是Java虛擬機規範中定義的內存區域,使用較頻繁)
服務器管理員在配置虛擬機參數時(如-Xmx等參數),如果忽略了直接內存,使各個內存區域總和大於物理內存限制(包括物理的和操作系統的限制),從而導致動態擴展時出現OutOfMemoryError異常。