1. 程式人生 > >JVM執行時資料區詳解

JVM執行時資料區詳解

在Java虛擬機器中,堆(Heap)是可供各條執行緒共享的執行時記憶體區域,也是供所有類例項和陣列物件分配記憶體的區域。

Java堆在虛擬機器啟動的時候就被建立,它儲存了被自動記憶體管理系統(Automatic Storage Management System,也即是常說的”Garbage Collector(垃圾收集器)”)所管理的各種物件,這些受管理的物件無需,也無法顯式地被銷燬。本規範中所描述的Java虛擬機器並未假設採用什麼具體的技術去實現自動記憶體管理系統。虛擬機器實現者可以根據系統的實際需要來選擇自動記憶體管理技術。Java堆的容量可以是固定大小的,也可以隨著程式執行的需求動態擴充套件,並在不需要過多空間時自動收縮。Java

堆所使用的記憶體不需要保證是連續的。

堆記憶體分為三部分:

永久儲存區

永久儲存區是一個常駐記憶體區域,用於存放JDK 自身所攜帶的Class,Interface 的元資料,也就是說它儲存的是執行環境必須的類資訊,被裝載進此區域的資料是不會被垃圾回收器回收掉的。關閉JVM 才會釋放此區域所佔用的記憶體

Young Generation Space 新生區

新生區是類的誕生、成長、消亡的區域,一個類在這裡產生,應用,最後被垃圾回收器收集,結束生命。新生區又分為兩部分: 伊甸區(Eden space)和倖存者區(Survivor pace),所有的類都是在伊甸區被new 出來的。倖存區有兩個: 0 區(Survivor 0 space

)和1 區(Survivor 1 space)。

當伊甸園的空間用完時,程式又需要建立物件,JVM 的垃圾回收器將對伊甸園區進行垃圾回收,將伊甸園區中的不再被其他物件所引用的物件進行銷燬。然後將伊甸園中的剩餘物件移動到倖存0區。若倖存0 區也滿了,再對該區進行垃圾回收,然後移動到1 區。那如果1 區也滿了呢?再移動到養老區。

Tenure generation space養老區

養老區用於儲存從新生區篩選出來的JAVA 物件,一般池物件都在這個區域活躍。 三個區的示意圖如下:

Method Area 方法區是被所有執行緒共享,該區域儲存所有欄位和方法位元組碼,以及一些特殊方法如建構函式,介面程式碼也在此定義。

PC Register 程式計數器, 每個執行緒都有一個程式計數器,就是一個指標,指向方法區中的方法位元組碼,由執行引擎讀取下一條指令。

Native Method Stack 本地方法棧,Java虛擬機器實現應當提供給程式設計師或者終端使用者調節Java堆初始容量的手段,對於可以動態擴充套件和收縮Java堆來說,則應當提供調節其最大、最小容量的手段。

Java堆可能發生如下異常情況,如果實際所需的堆超過了自動記憶體管理系統能提供的最大容量,那Java虛擬機器將會丟擲一個OutOfMemoryError異常。同樣通過程式碼來理解一下

        /**
	 * 迴圈建立新物件,構造<p>java.lang.OutOfMemoryError: Java heap space</p>異常
	 * 
	 * @author lihzh
	 * @alia OneCoder
	 * @blog http://www.coderli.com
	 */
	private static void getHeapOutOfMemoryError() {
		int count = 0;
		List list = new LinkedList<>();
		for (;;) {
			System.out.println(count++);
			list.add(new Object());
		}
	}

不停的建立新的物件,來使堆記憶體溢位。啟動引數設定:-Xmx2m

執行結果:

178702
178703
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

大概17w左右開始記憶體溢位。增大引數看看效果,-Xmx10m

305677
305678
305679
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

由此可見,通過設定-Xmx引數,可以調整JVM中堆的大小,從而可以在記憶體中儲存更多的物件。

再看看另一種OutOfMemoryError:java.lang.OutOfMemoryError: PermGen space。同樣是,通過程式碼構造一個PermGen space的異常。(OneCoder認為,你會構造出異常,自然就會慢慢理解這個異常是怎麼產生的,什麼引數會對這個有影響。)

由於PermGen space一般發生在預載入類的過程中,所以OneCoder這裡利用tomcat啟動來進行測試。設定Tomcat 啟動引數為:

-server -XX:MaxPermSize=2m -XX:PermSize=2m

執行結果

java.lang.OutOfMemoryError: PermGen space
		at sun.misc.URLClassPath$JarLoader$1.run(URLClassPath.java:608)

果然發生PermGen space異常。如果你逐漸放大該配置,會發現報錯的時機逐漸推後,也就是可以載入更多的類。OneCodertomcat指定一個合理的值比如:

-server -XX:MaxPermSize=64m -XX:PermSize=64m

程式即可重新啟動了。

你也可以多做一些試驗,驗證你的想法,所謂實踐出真知。

概念參考:

  • Java虛擬機器規範
  • 慢慢琢磨JVM