Java 中的堆是 JVM 管理的最大的一塊內存空間,主要用於存放Java類的實例對象,其被劃分爲兩個不同的區域:新生代 ( Young )和老年代 ( Old ),其中新生代 ( Young ) 又被劃分爲:Eden、From Survivor和To Survivor三個區域。
我們可以通過一段簡單的代碼來查看堆的空間劃分。
public class Jtest {
public static void main(String[] args) {
System.out.println("Hello,JVM!");
}
}
右鍵選擇 run configuration
再如下的地方添加 -Xms1024m -Xmx1024m -XX:+PrintGCDetails 代碼
控制檯輸出:
可以看出上面有幾個未知的單詞。而這幾個單詞正是代表了 堆的內存空間。
Java 中的堆是 JVM 管理的最大的一塊內存空間,主要用於存放Java類的實例對象,其被劃分爲兩個不同的區域:新生代 ( Young )和老年代 ( Old ),其中新生代 ( Young ) 又被劃分爲:Eden、From Survivor 和 To Survivor三個區域
從JDK8開始,Metaspace(元空間)替代了永久代,如下圖所示:
無論哪個版本的JDK,其堆內存的劃分都沒有變化,下面詳述Java堆中各個區域:
1、堆大小 = 新生代( Young ) + 老年代( Old ),其可以通過參數 –Xms、-Xmx 來指定:–Xms用於設置初始分配大小,默認爲物理內存的1/16;-Xmx用於設置最大分配內存,默認爲物理內存的1/4。默認情況下,新生代 ( Young ) 與老年代 ( Old ) 的比例的值爲 1:2 ,即:新生代 ( Young ) = 1/3 的堆空間大小,老年代 ( Old ) = 2/3 的堆空間大小
2、新生代 ( Young ) 被細分爲 Eden 和 兩個 Survivor 區域,爲了便於區分,兩個 Survivor 區域分別被命名爲 from 和 to。默認情況下,Eden : from : to = 8 : 1 : 1 ,即: Eden = 8/10 的新生代空間大小,from = to = 1/10 的新生代空間大小。JVM 每次只使用 Eden 和其中的一塊 Survivor 區域來爲對象服務,所以無論什麼時候,總是有一塊 Survivor 區域是空閒着的,因此,新生代實際可用的內存空間爲 9/10 ( 即90% )的新生代空間。
3 、工作原理:
a、Eden區爲Java對象分配堆內存,當 Eden 區沒有足夠空間分配時,JVM發起一次Minor GC,將Eden區仍然存活的對象放入Survivor from區,並清空Eden 區;
b、Eden區被清空後,繼續爲新的Java對象分配堆內存;
c、當Eden區再次沒有足夠空間分配時,JVM對Eden區和Survivor from區同時發起一次 Minor GC,把存活對象放入Survivor to區,同時清空Eden 區和Survivor from區;
d、Eden區繼續爲新的Java對象分配堆內存,並重覆上述過程:Eden區沒有足夠空間分配時,把Eden區和某個Survivor區的存活對象放到另一個Survivor區;
e、JVM給每個對象設置了一個對象年齡(Age)計數器,每熬過一場Minor GC,對象年齡增加1歲,當它的年齡增加到閾值(默認爲15),將被“晉升”到老年代,當 Old 區也被填滿時,JVM發起一次 Major GC,對 Old 區進行垃圾回收。
我們可以使用java 自帶的 java visualvm 來直觀的觀察 堆內存
我們首先打開visualvm 進行配置 工具-> 插件
然後我們去改一下插件下載地址。Java VisualVM默認的插件中心網址已關閉服務,插件中心的服務器已經移交到github上,地址爲https://visualvm.github.io/pluginscenters.html 去github 上尋找符合自己版本的下載地址粘貼上去。
更新好之後我們去可用插件中找VisualGC 下載。
然後我們啓動一個線程 並回到 java visualvm 打開GC 觀察
再圖中我們可以清晰的看到 Eden s0 s1 與 Old 的內存變化