jvm中內存划分:


如上圖,一共分為五塊,其中:
線程共享區域為:
1、java堆
2、方法區
線程私有區域為:
3、JVM棧
4、本地方法棧
5、程序計數器
各區域作用:
1、java堆:
java堆是jvm內存管理中最大的一塊,線程共享。在jvm啟動的時候創建。此區域唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內存。但是隨着JIT編譯器(即時編譯器)的發展與逃逸分析技術的逐漸成熟,棧上分配、標量替換優化技術將會導致一些微妙變化(對象可能會分配到棧上),所以這種所有對象都分配在堆上也不是那么絕對的。
java堆細分為新生代和老年代,新生代又分為Eden空間、From Survivor空間、To Survivor空間,新生代中垃圾回收算法為復制算法,復制算法是先將內存分為連個部分,一部分用來放入對象,而另一部分暫時不用,當使用的一部分內存要進行垃圾回收的時候會將不需要回收的對象復制保存在另一個空間中,然后再對使用過的那部分區域進行垃圾回收,這樣雖然效率很高,但是很浪費空間,所以一般將新生代分為Eden空間和兩個Survivor空間,其大小在HotSpot虛擬機中默認比例為8:1:1,這樣在新生代中采用復制算法回收垃圾效率就很高了,具體回收過程是將Eden區域和From Survivor區域作為對象的存儲空間,當要進行垃圾回收的時候先將這兩個區域中不需要回收的對象復制保存在To Survivor區域中,然后再進行垃圾回收。另外有一點是當一個對象在Eden區域和From Survivor區域中存儲的時候發現內存不足,這時會進行內存分配擔保,就是將此對象直接存入在老年代中。
老年代中采用的GC算法為標記-清除算法或者標記-整理算法。標記-清除算法為:首先標記出要進行GC的對象,標記完成后再進行GC。這種算法效率不高,並且會產生很多內存碎片。標記-整理算法:同樣是先對要進行GC的對象進行標記,但是不同的是在標記完成后不是立刻執行GC,而是先將不需要GC的對象移動到一端,然后在邊界外再對要回收的對象進行GC。
關於對象的分配:對象優先在Eden區域分配,大對象會直接進入老年代,長期存活的對象會進入老年代,這里的長期存活是根據新生代中的對象年齡閾值來定義的,對象剛分配到新生代的時候年齡為1,每進行一次GC對象的年齡會加1,HotSpot中默認的閾值是15,也就是說對象年齡達到15歲的時候會被分配到老年區,這個值是可以通過參數配置的。
在進行垃圾回收的時候新生代GC又叫minor GC,老年代GC可以設置內存容量達到百分比的多少的時候進行GC,老年代的GC又叫Full GC,minor GC時間短,頻率高,而Full GC時間長,頻率低。
2、方法區
方法區又被稱為永久區,線程共享,是用來存儲已被JVM加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。方法區為堆的一個邏輯部分,但是在JDK1.7的HotSpot中已經將方法區中的字符串常量池移出,部分資料顯示JDK1.8已經去除了方法區(不確定)。不過已經可以猜測此區域將會被本地內存逐步取代。
這個區域很少進行垃圾回收,回收目標主要是針對常量池的回收和對類型的卸載。
3、JVM棧
JVM棧是線程私有的,它的生命周期與線程相同。JVM棧描述的是java方法執行的內存模型,每個方法在執行的同時都會創建一個棧幀,用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧到出棧的過程。
局部變量表中存放了編譯期可知的各種基本數據類型、對象的引用類型。局部變量表中需要的內存空間在編譯期間完成分配,當進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。
4、本地方法棧
本地方法棧和JVM棧非常相似,它們之間的區別不過是jvm棧是為執行java方法服務,而本地方法棧是為jvm使用到對的本地方法服務。HotSpot虛擬機中直接把本地方法棧和JVM棧合二為一了。
5、程序計數器
程序計數器是一塊較小的內存空間,線程私有。它可以看作是當前線程所執行的字節碼的行號指示器。在jvm的概念模型里,字節碼解釋器工作就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。
如果線程正在執行的是一個java方法,這個計數器記錄的是正在執行的jvm字節碼指令的地址;如果正在執行的是本地方法,這個計數器值則為空。
總結:
在jvm划分的內存區域中JVM棧和本地方法棧可能會拋出StackOverflowError異常和OutOfMemoryError異常。java堆和方法區可能會拋出OutOfMemoryError異常。程序計數器中沒有地方規定會拋出這兩個異常。
擴展:
在jvm規范中,StackOverflowError異常為:如果線程請求的棧深度大於JVM允許的棧深度,將拋出StackOverflowError異常。OutOfMemoryError異常:如果jvm可以動態擴展,如果擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError異常。
HotSpot虛擬機中標記要清除的對象方法不是使用引用計數器(有引用的時候計數器+1,引用失效-1,應用為0時回收),而使用的是可達性分析算法:以“GC Roots“的對象為起點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,則證明這個對象不可達,即這個對象不可用,所以這個對象會被判定為是可回收對象。
標記-清除算法處理前后:


復制算法處理前后:


標記-整理算法處理前后:

