《java學習三》jvm效能優化-------調優

x號開發者發表於2019-03-22

常見引數配置

-XX:+PrintGC      每次觸發GC的時候列印相關日誌

-XX:+UseSerialGC      序列回收

-XX:+PrintGCDetails  更詳細的GC日誌

-Xms               堆初始值

-Xmx               堆最大可用值

-Xmn               新生代堆最大可用值

-XX:SurvivorRatio  用來設定新生代中eden空間和from/to空間的比例.

-XX:NewRatio       配置新生代與老年代佔比 1:2

含以-XX:SurvivorRatio=eden/from=den/to

總結:在實際工作中,我們可以直接將初始的堆大小與最大堆大小相等,

這樣的好處是可以減少程式執行時垃圾回收次數,從而提高效率。

-XX:SurvivorRatio     用來設定新生代中eden空間和from/to空間的比例.

堆記憶體大小配置

使用示例:  -Xmx20m -Xms5m 

說明: 當下Java應用最大可用記憶體為20M, 初始記憶體為5M

// byte[] b = new byte[4 * 1024 * 1024];

// System.out.println("分配了4M空間給陣列");

System.out.print("最大記憶體");

System.out.println(Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");

System.out.print("可用記憶體");

System.out.println(Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");

System.out.print("已經使用記憶體");

System.out.println(Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");

 

 

設定新生代比例引數

使用示例:-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

說明:堆記憶體初始化值20m,堆記憶體最大值20m,新生代最大值可用1m,eden空間和from/to空間的比例為2/1

byte[] b = null;

for (int i = 0; i < 10; i++) {

b = new byte[1 * 1024 * 1024];

}

 


使用示例: -Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
設定新生代與老年代比例引數

-XX:NewRatio=2

說明:堆記憶體初始化值20m,堆記憶體最大值20m,新生代最大值可用1m,eden空間和from/to空間的比例為2/1

新生代和老年代的佔比為1/2

 

實戰OutOfMemoryError異常

 

Java堆溢位

錯誤原因: java.lang.OutOfMemoryError: Java heap space 堆記憶體溢位

解決辦法:設定堆記憶體大小 // -Xms1m -Xmx10m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError

// -Xms1m -Xmx10m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError

List<Object> listObject = new ArrayList<>();

for (int i = 0; i < 10; i++) {

System.out.println("i:" + i);

Byte[] bytes = new Byte[1 * 1024 * 1024];

listObject.add(bytes);

}

System.out.println("新增成功...");

 


虛擬機器棧溢位
 

錯誤原因: java.lang.StackOverflowError  棧記憶體溢位

棧溢位 產生於遞迴呼叫,迴圈遍歷是不會的,但是迴圈方法裡面產生遞迴呼叫, 也會發生棧溢位。

解決辦法:設定執行緒最大呼叫深度

-Xss5m 設定最大呼叫深度

publicclass JvmDemo04 {

       privatestaticintcount;

       publicstaticvoid count(){

           try {

                  count++;

                  count();

           } catch (Throwable e) {

                 System.out.println("最大深度:"+count);

                 e.printStackTrace();

           }

       }

       publicstaticvoid main(String[] args) {

            count();

      }

}

 

 

  

記憶體溢位與記憶體洩漏區別

Java記憶體洩漏就是沒有及時清理記憶體垃圾,導致系統無法再給你提供記憶體資源(記憶體資源耗盡);

而Java記憶體溢位就是你要求分配的記憶體超出了系統能給你的,系統不能滿足需求,於是產生溢位。

記憶體溢位,這個好理解,說明儲存空間不夠大。就像倒水倒多了,從杯子上面溢位了來了一樣。
記憶體洩漏,原理是,使用過的記憶體空間沒有被及時釋放,長時間佔用記憶體,最終導致記憶體空間不足,而出現記憶體溢位。

 

垃圾收集器

序列與並行收集器

序列回收: JDK1.5前的預設演算法 缺點是隻有一個執行緒,執行垃圾回收時程式停止的時間比較長

並行回收: 多個執行緒執行垃圾回收適合於吞吐量的系統,回收時系統會停止執行

 

 

serial收集器

序列收集器是最古老,最穩定以及效率高的收集器,可能會產生較長的停頓,只使用一個執行緒去回收。新生代、老年代使用序列回收;新生代複製演算法、老年代標記-壓縮;垃圾收集的過程中會Stop The World(服務暫停)

 

一個單執行緒的收集器,在進行垃圾收集時候,必須暫停其他所有的工作執行緒直到它收集結束。

特點:CPU利用率最高,停頓時間即使用者等待時間比較長。

適用場景:小型應用

通過JVM引數-XX:+UseSerialGC可以使用序列垃圾回收器。

 

ParNew收集器

 

ParNew收集器其實就是Serial收集器的多執行緒版本。新生代並行,老年代序列;新生代複製演算法、老年代標記-壓縮

引數控制:-XX:+UseParNewGC  ParNew收集器

-XX:ParallelGCThreads 限制執行緒數量

parallel 收集器

Parallel Scavenge收集器類似ParNew收集器,Parallel收集器更關注系統的吞吐量。可以通過引數來開啟自適應調節策略,虛擬機器會根據當前系統的執行情況收集效能監控資訊,動態調整這些引數以提供最合適的停頓時間或最大的吞吐量;也可以通過引數控制GC的時間不大於多少毫秒或者比例;新生代複製演算法、老年代標記-壓縮

 

採用多執行緒來通過掃描並壓縮堆
特點:停頓時間短,回收效率高,對吞吐量要求高。
適用場景:大型應用,科學計算,大規模資料採集等。
通過JVM引數 XX:+USeParNewGC 開啟併發標記掃描垃圾回收器。

 

cms收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器。目前很大一部分的Java應用都集中在網際網路站或B/S系統的服務端上,這類應用尤其重視服務的響應速度,希望系統停頓時間最短,以給使用者帶來較好的體驗。

從名字(包含“Mark Sweep”)上就可以看出CMS收集器是基於“標記-清除”演算法實現的,它的運作過程相對於前面幾種收集器來說要更復雜一些,整個過程分為4個步驟,包括:

初始標記(CMS initial mark)

併發標記(CMS concurrent mark)

重新標記(CMS remark)

併發清除(CMS concurrent sweep)

其中初始標記、重新標記這兩個步驟仍然需要“Stop The World”。初始標記僅僅只是標記一下GC Roots能直接關聯到的物件,速度很快,併發標記階段就是進行GC Roots Tracing的過程,而重新標記階段則是為了修正併發標記期間,因使用者程式繼續運作而導致標記產生變動的那一部分物件的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比並發標記的時間短。

   由於整個過程中耗時最長的併發標記和併發清除過程中,收集器執行緒都可以與使用者執行緒一起工作,所以總體上來說,CMS收集器的記憶體回收過程是與使用者執行緒一起併發地執行。老年代收集器(新生代使用ParNew)

 優點:併發收集、低停頓

缺點:產生大量空間碎片、併發階段會降低吞吐量

 

採用“標記-清除”演算法實現,使用多執行緒的演算法去掃描堆,對發現未使用的物件進行回收。

(1)初始標記

(2)併發標記

(3)併發預處理

(4)重新標記

(5)併發清除

(6)併發重置

特點:響應時間優先,減少垃圾收集停頓時間

適應場景:大型伺服器等。

通過JVM引數 -XX:+UseConcMarkSweepGC設定

g1收集器

在G1中,堆被劃分成 許多個連續的區域(region)。採用G1演算法進行回收,吸收了CMS收集器特點。

特點:支援很大的堆,高吞吐量

  --支援多CPU和垃圾回收執行緒

  --在主執行緒暫停的情況下,使用並行收集

  --在主執行緒執行的情況下,使用併發收集

實時目標:可配置在N毫秒內最多隻佔用M毫秒的時間進行垃圾回收

通過JVM引數 -XX:+UseG1GC 使用G1垃圾回收器

注意: 併發是指一個處理器同時處理多個任務。 
並行是指多個處理器或者是多核的處理器同時處理多個不同的任務。 
併發是邏輯上的同時發生(simultaneous),而並行是物理上的同時發生。 
來個比喻:併發是一個人同時吃三個饅頭,而並行是三個人同時吃三個饅頭。 

 

Jmeter壓力測試工具

 

 

調優總結

初始堆值和最大堆記憶體記憶體越大,吞吐量就越高。

最好使用並行收集器,因為並行收集器速度比序列吞吐量高,速度快。

設定堆記憶體新生代的比例和老年代的比例最好為1:2或者1:3。

減少GC對老年代的回收。

相關文章