JVM之---Java內存結構(第三篇)

        通過前兩節,我們瞭解到了java的大概內存結構,其實JVM的內存分佈還是比較複雜的,並且各個java虛擬機不相同,內部結構會有相應的變化,有些時候我們將其理解爲概念模型纔不會有太多的煩惱,雖然前面的文字描述了那麼多關於內存模型的東西,但是可能在你現在安裝的JVM中未必就完全按照如此進行分佈,需要視具體的版本而定。

爲什麼還要有這一小節的存在呢?本來想要開始java垃圾回收的文章,但是在整理垃圾回收相關資料的時候,我又決定,需要對堆內存做進一步的劃分說明(其實這本身是JVM對垃圾回收的一個規範模型),這樣,在理解起來以後的垃圾回收機制就不會顯得有那麼多的陌生感。

本節中將有如下的一些內容進行說明:

1、堆內存的分佈

2、通過jps和jstat命令查看堆內存的情況

3、通過一個dump信息查看堆內存的情況

第一:java堆內存的分佈

java的堆內存還可以進一步的細分,現在比較主流的劃分爲Eden,兩個Survivor from,Survivor to ,permanent,tenured,如下圖所示,其中Eden,兩個Survivor是在新生代中,permanent和tenured在老年代中,什麼是新生代和老年代,說的通俗一點就是一個是存放剛創建對象的內存空間,一個是存放早期創建對象的內存空間,如下圖所示:

上面的圖,其中From Survivor 單詞寫反了,To Survivor 也寫反了,發佈之後才注意到)

解釋一下上面的圖,當創建一個對象的時候,該對象將被存放到Eden區,如果經過了一次垃圾回收,Eden有存活的對象,將會被存放到Survivor區,也就是說Survivor存放的是新一輪GC開始之前存活的對象,如果對象在Survivor中經常使用或者說Survivor空間不足,會將對象放到老年代也就是Tenured區,如果在垃圾回收之後,Survivor沒有足夠的空間,需要向老年代申請空間的時候,老年代此時也是由於空間不夠,此時將會拋出OutOfMemoryError,換言之,堆中經常出現內存溢出的區域是兩個Survivor和Tenured區。

第二:jps和jstat命令介紹

我們設計一個程序,使其最終發生堆內存溢出,然後在此期間不斷地觀察堆內存的變化,其中要使用到兩個jdk自帶的命令,其中jps是查看當前的jvm進程,而jstat是查看堆內存的變化情況。
      其中jps是一個比較簡單的命令,基本上和linux下的ps -ef|grep java沒有什麼太大的區別,在這裏就不作爲重點來說了,在後文的jps信任檢測工具,我們也會專門進行jdk自帶命令的詳解;
      這裏需要重點所一下的是jstat命令,格式爲:jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
     其中option的選項有如下許多:
1、-class 監視類裝載的情況,數量,內存浪費時間等;
2、-gc  監視堆的狀況
3、-gccapacity 和gc類似
4、-gcutil  和-gc類似但是輸出已使用的空間
5、-gccause 和-gcutil類似
6、-gcnew 檢測新生代
7、-gcnewcapacity 和-gcnew類似
8、-gcold 檢測老年代
9、-gcoldcapacity 和-gcold類似
10、-gcpermcapacity 輸出永久代
11、-compiler 輸出JIT編譯器過的方法耗時等信息
12、-printcompliation 輸出已輸入的JIT編譯的方法

第三:生成dump並且簡單說明

      好了,命令暫時說道這裏,在這快,我們做一個堆內存溢出的實驗,並且進行jstat分析,然後看一下其中堆內存空間的變化情況。看看如下的java代碼:
import java.util.List;
import java.util.ArrayList;

/**
 *@desc 進行堆內存溢出的測試
 *@author wangwenjun(QQ:532500648)
 *@since 1.0.0
 * */
public class HeapMemoryTest
{

	static class Test{}

	public static void main(String[] args)
	{
		List list = new ArrayList();
		/*休眠時間長一點,是爲了執行jps和jstat命令先*/
		try{
				Thread.sleep(10000);
		}catch(Exception e){};
		
		while(true)
		{
			list.add(new Test());
			try{
				Thread.sleep(2);
				/*休眠,能更好的查看到內存的變化*/
			}catch(Exception e){};
		}
	}
}

編譯好之後,使用如下命令執行:
java -verbose:gc -Xms10M -Xmx10M -XX:+PrintGCDetails HeapMemoryTest
我們打開兩個窗口,第一個窗口用來運行class文件,另外一個用來執行jstat命令:jstat -gc <pid> 1000 1000(查看pid的內存信息),分別進行了兩次採樣,其中第一次但Eden區快要滿的時候的截圖如下所示

      第二次Eden區快要滿的時候的截圖,我們可以看到上一次中內存分佈發生瞭如下的變化,s1區被填滿,並且老年代中的空間也多了445.6

      當發生gc的時候,我們暴力的然jvm輸出了dump信息,如下所示,分別是兩次GC的截圖(最後一個是我進行關閉時候的截圖)



     java內存大致就到這裏,在以後的垃圾回收併發編程中我們再進行更爲詳細的介紹。


發佈了32 篇原創文章 · 獲贊 21 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章