要了解Java垃圾收集機制,先理解JVM內存模式是很是重要的。今天咱們將會了解JVM內存的各個部分、如何監控以及垃圾收集調優。html
正如你從上面的圖片看到的,JVM內存被分紅多個獨立的部分。普遍地說,JVM堆內存被分爲兩部分——年輕代(Young Generation)和老年代(Old Generation)。java
年輕代是全部新對象產生的地方。當年輕代內存空間被用完時,就會觸發垃圾回收。這個垃圾回收叫作Minor GC。年輕代被分爲3個部分——Enden區和兩個Survivor區。算法
年輕代空間的要點:shell
年老代內存裏包含了長期存活的對象和通過屢次Minor GC後依然存活下來的對象。一般會在老年代內存被佔滿時進行垃圾回收。老年代的垃圾收集叫作Major GC。Major GC會花費更多的時間。服務器
全部的垃圾收集都是「Stop the World」事件,由於全部的應用線程都會停下來直到操做完成(因此叫「Stop the World」)。多線程
由於年輕代裏的對象都是一些臨時(short-lived )對象,執行Minor GC很是快,因此應用不會受到(「Stop the World」)影響。併發
因爲Major GC會檢查全部存活的對象,所以會花費更長的時間。應該儘可能減小Major GC。由於Major GC會在垃圾回收期間讓你的應用反應遲鈍,因此若是你有一個須要快速響應的應用發生屢次Major GC,你會看到超時錯誤。oracle
垃圾回收時間取決於垃圾回收策略。這就是爲何有必要去監控垃圾收集和對垃圾收集進行調優。從而避免要求快速響應的應用出現超時錯誤。jsp
永久代或者「Perm Gen」包含了JVM須要的應用元數據,這些元數據描述了在應用裏使用的類和方法。注意,永久代不是Java堆內存的一部分。ide
永久代存放JVM運行時使用的類。永久代一樣包含了Java SE庫的類和方法。永久代的對象在full GC時進行垃圾收集。
方法區是永久代空間的一部分,並用來存儲類型信息(運行時常量和靜態變量)和方法代碼和構造函數代碼。
若是JVM實現支持,JVM內存管理會爲建立內存池,用來爲不變對象建立對象池。字符串池就是內存池類型的一個很好的例子。內存池能夠屬於堆或者永久代,這取決於JVM內存管理的實現。
運行時常量池是每一個類常量池的運行時表明。它包含了類的運行時常量和靜態方法。運行時常量池是方法區的一部分。
Java棧內存用於運行線程。它們包含了方法裏的臨時數據、堆裏其它對象引用的特定數據。你能夠閱讀棧內存和堆內存的區別。
Java提供了大量的內存開關(參數),咱們能夠用它來設置內存大小和它們的比例。下面是一些經常使用的開關:
VM 開關 | VM 開關描述 |
---|---|
-Xms | 設置JVM啓動時堆的初始化大小。 |
-Xmx | 設置堆最大值。 |
-Xmn | 設置年輕代的空間大小,剩下的爲老年代的空間大小。 |
-XX:PermGen | 設置永久代內存的初始化大小。 |
-XX:MaxPermGen | 設置永久代的最大值。 |
-XX:SurvivorRatio | 提供Eden區和survivor區的空間比例。好比,若是年輕代的大小爲10m而且VM開關是-XX:SurvivorRatio=2,那麼將會保留5m內存給Eden區和每一個Survivor區分配2.5m內存。默認比例是8。 |
-XX:NewRatio | 提供年老代和年輕代的比例大小。默認值是2。 |
大多數時候,上面的選項已經足夠使用了。可是若是你還想了解其餘的選項,那麼請查看JVM選項官方網頁。
Java垃圾回收會找出沒用的對象,把它從內存中移除並釋放出內存給之後建立的對象使用。Java程序語言中的一個最大優勢是自動垃圾回收,不像其餘的程序語言那樣須要手動分配和釋放內存,好比C語言。
垃圾收集器是一個後臺運行程序。它管理着內存中的全部對象並找出沒被引用的對象。全部的這些未引用的對象都會被刪除,回收它們的空間並分配給其餘對象。
一個基本的垃圾回收過程涉及三個步驟:
簡單標記和清除方法存在兩個問題:
上面簡單清除方法的問題在於Java垃圾收集的分代回收的,並且在堆內存裏有年輕代和年老代兩個區域。我已經在上面解釋了Minor GC和Major GC是怎樣掃描對象,以及如何把對象從一個分代空間移到另一個分代空間。
這裏有五種能夠在應用裏使用的垃圾回收類型。僅須要使用JVM開關就能夠在咱們的應用裏啓用垃圾回收策略。讓咱們一塊兒來逐一瞭解:
咱們可使用命令行和圖形工具來監控監控應用垃圾回收。例如,我使用Java SE下載頁中的一個demo來實驗。
若是你想使用一樣的應用,能夠到Java SE下載頁面下載JDK 7和JavaFX演示和示例。我使用的示例應用是Java2Demo.jar,它位於 jdk1.7.0_55/demo/jfc/Java2D 目錄下。這只是一個可選步驟,你能夠運行GC監控命令監控任何Java應用。
我打開演示應用使用的命令是:
1
|
pankaj@Pankaj:~
/Downloads/jdk1
.7.0_55
/demo/jfc/Java2D
$ java -Xmx120m -Xms30m -Xmn10m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar Java2Demo.jar
|
可使用jstat命令行工具監控JVM內存和垃圾回收。標準的JDK已經附帶了jstat,因此不須要作任何額外的事情就能夠獲得它。
要運行jstat你須要知道應用的進程id,你可使用 ps -eaf | grep java 命令獲取進程id。
1
2
3
|
pankaj@Pankaj:~$
ps
-eaf |
grep
Java2Demo.jar
501 9582 11579 0 9:48PM ttys000 0:21.66
/usr/bin/java
-Xmx120m -Xms30m -Xmn10m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseG1GC -jar Java2Demo.jar
501 14073 14045 0 9:48PM ttys002 0:00.00
grep
Java2Demo.jar
|
從上面知道,個人Java應用進程id是9582。如今能夠運行jstat命令了,就像下面展現的同樣:
1
2
3
4
5
6
7
8
9
|
pankaj@Pankaj:~$ jstat -gc 9582 1000
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
1024.0 1024.0 0.0 0.0 8192.0 7933.3 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
1024.0 1024.0 0.0 0.0 8192.0 8026.5 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
1024.0 1024.0 0.0 0.0 8192.0 8030.0 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
1024.0 1024.0 0.0 0.0 8192.0 8122.2 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
1024.0 1024.0 0.0 0.0 8192.0 8171.2 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
1024.0 1024.0 48.7 0.0 8192.0 106.7 42108.0 23401.3 20480.0 19990.9 158 0.275 40 1.381 1.656
1024.0 1024.0 48.7 0.0 8192.0 145.8 42108.0 23401.3 20480.0 19990.9 158 0.275 40 1.381 1.656
|
jstat命令的最後一個參數是每一個輸出的時間間隔。每隔一秒就會打印出內存和垃圾收集數據。
讓咱們一塊兒來對每一列的意義進行逐一瞭解:
jstat的優勢,咱們一樣能夠在沒有GUI的遠程服務器上運行jstat。注意:咱們是經過 -Xmn10m 選項來指定S0C、S1C和EC的總和爲10m的。
若是你想在GUI裏查看內存和GC,那麼可使用jvisualvm工具。Java VisualVM一樣是JDK的一部分,因此你不須要單獨去下載。
在終端運行jvisualvm命令啓動Java VisualVM程序。一旦啓動程序,你須要從Tools->Plugins選項安裝Visual GC插件,就像下面圖片展現的。
安裝完Visual GC插件後,從左邊欄打開應用並把視角轉到Visual GC部分。你將會獲得關於JVM內存和垃圾收集詳情,以下圖所示。
Java垃圾回收調優應該是提高應用吞吐量的最後一個選擇。在你發現應用因爲長時間垃圾回收致使了應用性能降低、出現超時的時候,應該考慮Java垃圾收集調優。
若是你在日誌裏看到 java.lang.OutOfMemoryError: PermGen space錯誤,那麼能夠嘗試使用 -XX:PermGen 和 -XX:MaxPermGen JVM選項去監控並增長Perm Gen內存空間。你也能夠嘗試使用-XX:+CMSClassUnloadingEnabled並查看使用CMS垃圾收集器的執行性能。
若是你看到了大量的Full GC操做,那麼你應該嘗試增大老年代的內存空間。
全面垃圾收集調優要花費大量的努力和時間,這裏沒有一塵不變的硬性調優規則。你須要去嘗試不一樣的選項而且對這些選項進行對比,從而找出最適合本身應用的方案。
這就是全部的Java內存模型和垃圾回收內容。但願對你理解JVM內存和垃圾收集過程有所幫助。