http://blog.csdn.net/zhoutao198712/article/details/7783070java
到目前爲止,尚未作明確的優化工做。只是作了初始化選擇工做,好比說:JVM部署模型、JVM運行環境、收集哪些垃圾回收器的信息以及須要遵照垃圾回收原則。這一步將介紹如何評估應用須要的內存大小以及Java堆大小。首先須要判斷出應用存活的數據的大小,存活數據的大小是決定配置應用須要的Java堆大小的重要條件,也可以決定是否須要從新審視一下應用的內存需求或者修改應用程序以知足內存需求。linux
注意:存活數據是指,應用處於穩定運行狀態下,在Java堆裏面長期存活的對象。換一句話說,就是應用在穩定運行的狀態下,Full GC以後,Java堆的所佔的空間。緩存
約束工具
有多少物理內存能夠供JVM使用?是部署多個JVM或者單個JVM?對作出的決定有重要影響。下面列出了一些要點能夠幫助決定有多少物理內存能夠供使用。性能
一、一個機器上面只是部署一個JVM,且就一個應用使用?若是是這種狀況,那麼機器的全部物理內存能夠供JVM使用。測試
二、一個機器上部署了多個JVM?或者一個機器上部署了多個應用?若是是這兩個中的任何一種狀況,你就必需要決定每個JVM或者應用須要分配多少內存了。優化
不管是前面的哪一種狀況,都須要給操做系統留出一些內存。spa
HotSpot VM的堆結構操作系統
在作內存佔用測量以前,咱們必需要先理解HotSpot VM Java堆的結構,理解這個對決定應用須要的Java堆大小以及優化垃圾收器性能有很好的幫助。.net
HotSpot VM有3個主要的空間:young代、old代以及permanent代,如上圖所示。
當Java應用分配Java對象時,這些對象被分配到young代。在經歷過幾回minor GC以後,若是對象仍是存活的,就會被轉移到old代空間。permanent代空間存儲了VM和Java類的元數據好比內置的字符串和類的靜態變量。
-Xmx和-Xms這個兩個命令行選項分別指定yound代加上old代空間的總和的初始最大值和最小值,也就是Java堆的大小。當-Xms的值小於-Xmx的值的時候,Java堆的大小能夠在最大值和最小值以前浮動。當Java應用強調吞吐量和延遲的時候,傾向於把-Xms和-Xmx設置成相同的值,因爲調整young代或者old代的大小都須要進行Full GC,Full GC下降吞吐量以及增強延遲。
young代的空間能夠經過下面的任意一個命令行選項來設置:
young代的初始值和最小值。<n>是大小,[g|m|k]表示單位是G字節,M字節或者千字節。young代的大小不會小於這個值。當設定-XX:NewSize=<n>[g|m|k]的時候,-XX:MaxNewSize=<n>[g|m|k]須要被指定。
young區空間的最大值。同上面反過來,當指定-XX:MaxNewSize=<n>[g|m|k]的須要指定-XX:NewSize=<n>[g|m|k]。
直接指定young代的初始值、最小值以及最大值。也就是說,young區的大小被固定成這個值了。這個值用來鎖定young代的大小很方便。
有一點須要注意的是,若是-Xms和-Xmx沒有被設定成相同的值,並且-Xmn被使用了,當調整Java堆的大小的時候,不會調整young代的空間大小,young代的空間大小會保持恆定。所以,-Xmn應該在-Xms和-Xmx設定成相同的時候才指定。
old代的空間大小能夠基於young代的大小進行計算,old代的初始值的大小是-Xms的值減去-XX:NewSize,最大值是-Xmx減去-XX:MaxNewSize,若是-Xmx和-Xms設置成了相同的值,並且使用-Xmn選項或者-XX:NewSize和-XX:MaxNewSize設置成了相同的值,那麼old代的大小就是-Xmx減去-Xmn。
permanent代的大小經過下面命令行參數指定
表示permanent代的初始值和最小值,n表示數值,g|m|k表示單位、permanent的空間必定不會比這個空間小。
permanent代的最大值,permanent代的大小不會超過這個值。
Java應用應該指定這兩個值成爲同一個值,因爲這個值的調整會致使Full GC。
若是上面提到的Java堆大小、young代、permanent代的大小都沒有指定,那麼JVM會根據應用的狀況自行計算。
在young代、old代以及permanent代中任何一個空間裏面沒法分配對象的時候就會觸發垃圾回收,理解這點,對後面的優化很是重要。當young代沒有足夠空間分配Java對象的時候,觸發minor GC。minor GC相對於Full GC來講會更短暫。
一個對象在經歷過必定次數的Minor GC以後,若是還存活,那麼會被轉移到old代(對象有一個「任期閥值」的概念,優化延遲的時候再介紹)。當old代沒有足夠空間放置對象的時候,HotSpot VM觸發full GC。實際上在進行Minor GC的時候發現沒有old代足夠的空間來進行對象的轉移,就會觸發FullGC,相對於在MinorGC的過程當中發現對象的移動失敗了而後觸發FullGC,這樣的策略會有更小的花費。當permanent代的空間不夠用的時候的,也會觸發FullGC。
若是FullGC是因爲old代滿了而觸發的,old代和permanent代的空間都會被垃圾回收,即便permanent代的空間尚未滿。同理,若是FullGC是因爲permanent代滿了而觸發的,old代和permanent代的空間都會被垃圾回收,即便old代的空間尚未滿。另外,young代一樣會被垃圾回收,除非-XX:+ScavengeBeforeFullGC選項被指定了,-XX:+ScavengeBeforeFullGC關閉FullGC的時候young代的垃圾回收。
堆大小優化的起點
爲了進行Java堆大小的優化,一個合適的起點很重要。這節描述的方案是須要先使用比應用須要量更大的Java堆做爲開始。這一步的目的是收集一些初始化信息以及爲了進一步優化Java堆大小須要的數據。
就像在「選擇JVM runtime」小節裏面提到過的,由吞吐量垃圾回收器(throughput garbage collector)開始。記住,使用吞吐量垃圾回收器經過設置-XX:+UserParallelOldGC命令行選項,若是你使用的HotSpot VM不支持的這個選項,那麼就使用-XX:+UserParallelGC。
若是你可以準確的預估到應用須要消耗的Java堆空間,能夠經過設定-Xmx和-Xms來做爲這個步驟的起點。若是你不知道該設定什麼值,就讓JVM來選擇吧,反正後面,都會根據實際狀況進行優化調整。
關於如何監控GC日誌前面的「GC優化基礎」已經描述過了。GC日誌會展現在使用中的java堆的大小。初始化和最大的堆大小能夠經過-XX:+PrintCommandLineFlags來查看。-XX:+PrintCommandLineFlags打印出在HotSpot VM初始化的時候選擇的初始值和最大值好比-XX:InitialHeapSize=<n> -XX:MaxHeapSize=<m>,這裏n表示初始化的java堆大小值,m表示java堆的最大值。
無論你是指定java堆的大小仍是使用默認的大小,必須讓應用進入穩定運行的狀態,你必需要有能力和手段讓應用處於和線上穩定運行的狀態相同的狀態。
若是在企圖讓應用進入穩定狀態的時候,你在垃圾回收日誌裏面觀察到OutOfMemoryError,注意是old代溢出仍是permanent代溢出。下面一個old代溢出的例子:
上面重要的部分加粗標示了,因爲使用的是吞吐量垃圾回收器,old代的統計信息標示爲ParOldGen。這行表示了old代的在FullGC的時候佔用的空間。從這個結果來看,能夠得出的結論是old代的空間過小了,因爲FullGC先後old代的被佔用的空間和分配的空間基本相等了,所以,JVM報了OutOfMemoryError。相比較,經過PSPermGen這行能夠看出permanent代的空間佔用是32390K,和他的容量(65536K)比仍是有必定的距離。
下面的例子展現了因爲permanent太少了而致使的OutOfMemoryError發生的例子
同上面同樣,把關鍵行標示出來了,經過PSPermGen這行能夠看出在FullGC先後,他的空間佔用量都和他的容量相同,能夠得出的結論是permanent代的空間條小了,這樣就致使了OutOfMemoryError。在這個例子裏面,old的佔用空間(132538K)遠比他的容量(350208K)小。
若是在垃圾回收日誌中觀察到OutOfMemoryError,嘗試把Java堆的大小擴大到物理內存的80%~90%。尤爲須要注意的是堆空間致使的OutOfMemoryError以及必定要增長空間。好比說,增長-Xms和-Xmx的值來解決old代的OutOfMemoryError,增長-XX:PermSize和-XX:MaxPermSize來解決permanent代引發的OutOfMemoryError。記住一點Java堆可以使用的容量受限於硬件以及是否使用64位的JVM。在擴大了Java堆的大小以後,再檢查垃圾回收日誌,直到沒有OutOfMemoryError爲止。
若是應用運行在穩定狀態下沒有OutOfMemoryError就能夠進入下一步了,計算活動對象的大小。
計算活動對象的大小
就像前面提到的,活動對象的大小是應用處於穩定運行狀態時,長時間存活數據佔用的Java堆的空間大小。換句話說,就是應用穩定運行是,在FullGC以後,old代和permanent代的空間大小。
活動對象的大小能夠經過垃圾回收日誌查看,它提供了一些優化信息,以下:
一、應用處於穩定運行狀態下,old代的Java堆空間佔用數量。
二、應用處於穩定運行狀態下,permanent代的Java堆空間佔用數量。
爲了保證可以準確的評估應用的活動對象大小,最好的作法是多看幾回FullGC以後Java堆空間的大小,保證FullGC是發生在應用處於穩定運行的狀態。
若是應用沒有發生FullGC或者發生FullGC的次數不多,在性能測試環境,能夠經過Java監控工具來觸發FullGC,好比使用VisualVM和JConsole,這些工具在最新的JDK的bin目錄下能夠找到,VisualVM集成了JConsole,VisualVM或者JConsole上面有一個觸發GC的按鈕。
另外,jmap命令能夠選擇來強制HotSpot VM進行FullGC。jmap 須要-histo:live命令選項以及JVM進程id。JVM的進程id能夠經過jps命令獲取。好比JVM的進程id是348,jmap命令用來觸發FullGC能夠想以下這樣寫:
jmap不只僅觸發FullGC,並且產生堆的關於對象分配的概要信息。不過就目前這步的目的而言,能夠忽略產生的堆概要信息。
初始化堆大小配置
本節描述了怎樣利用活動對象的大小來決定初始化的Java堆的大小。下面的圖,給出了應用存活的對象的大小。比較明智的作法是多收集幾回FullGC信息,有更多的信息,可以作出更加好的決定。
經過活動對象大小的信息,能夠作出關於Java堆的大小有根據的決定,以及能夠估計出最壞狀況下會致使的延遲。
比較常規是,Java堆大小的初始化值和最大值(經過-Xms和-Xmx選項來指定)應該是old代活動對象的大小的3到4倍。
在上圖中顯示的FullGC信息中,在FullGC以後old代的大小是295111K,差很少是295M,即活動的對象的大小是295M。所以,推薦的Java堆的初始化和最大值應該是885M到1180M,便可以設置爲-Xms885m -Xmx1180m。在這個例子中,Java堆的大小是1048570K差很少1048M,在推薦值範圍內。
另一個常規是,permanent的初始值和最大值(-XX:PermSize和-XX:MaxPermSize)應該permanent代活動對象大小的1.2到1.5倍。在上圖中看到在FullGC以後permanent代佔用空間是32390K,差很少32M。所以,permanent代的推薦大小是38M到48M,便可以設置爲-XX:PermSize=48m -XX:MaxPermSize=48m(1.5倍)。這個例子裏面,permanent代的空間大小是65536K即64M,大出了17M,不過在1G內存的系統的中,這個數值徹底能夠忍受。
另一個常規是,young代空間應該是old代活動對象大小的1到1.5倍。那麼在這裏例子中,young代的大小能夠設置爲295M到442M。本例裏面,young代的空間大小的358400K,差很少358M,在推薦值中間。
若是推薦的Java堆的初始值和最大值是活動對象大小3到4倍,而young代的推薦只是1到1.5倍,那麼old代空間大小應該是2到3倍。
經過以上規則,咱們可使用的Java命令能夠是這樣的:
另一些考慮
本節將說起到在進行應用內存佔用評估的時候,另一些須要記住的點。首先,必需要知道,前面只是評估的Java堆的大小,而不是Java應用佔用的全部的內存,若是要查看Java應用佔用的全部內存在linux下能夠經過top命令查看或者在window下面經過任務管理器來查看,儘管Java堆的大小可能對Java應用佔用內存作出了最大的貢獻。 好比說,爲了存儲線程堆棧,應用須要額外的內存,越多的線程,越多內存被線程棧消耗,越深的方法間調用,線程棧越多。另外,本地庫須要分配額外的內存,I/O緩存也須要額外的內存。應用的內存消耗須要評估到應用任何一個會消耗內存的地方。
記住,這一步操做不必定可以知足應用內存消耗的需求,若是不能知足,就回過頭來看需求是否合理或者修改應用程序。比較可行的一種辦法是修改應用程序減少對象的分配,從而減小內存的消耗。
Java堆的大小計算僅僅只是開始,根據需求,在後面的優化步驟中可能會修改。