[轉]JVM調優原理與常見異常處理方案
在jvm調優之前,我們必須先了解jvm的記憶體模型與GC回收機制,這些在我前面的文章裡面有介紹!接下來我們通過一個案例來調整jvm效能。
一 測試案例:
1.1 編寫demo
import java.text.DecimalFormat;
/**
-XX:+PrintGC 列印GC日誌
-XX:+PrintGCDetails 列印詳細的GC日誌
file.encoding 檔案編碼
-XX:MaxTenuringThreshold 物件年齡,預設15次之後較大機率放進入老年代,為0則不進入s0 s1區,直接進老年代
-XX:+UseSerialGC 序列收集器
-XX:+UseParallelOldGC 並行收集器
-XX:+UseParallelGC 合併回收
-XX:ParallelGCThreads 並行收集器執行緒數量
-Xms 堆初始值
-Xmx 堆最大可用值
-XX:SurvivorRatio 新生代中eden空間和from(s0),to(s1)空間的比例
-XX:NewRatio 新生代與老年代的比例
-Xss 棧的大小(棧的深度)
-XX:MetaspaceSize 元空間初始值
-XX:MaxMetaspaceSize 元空間最大值
-XX:+HeapDumpOnOutOfMemoryError:記憶體溢位生成快照檔案
*/
public class JVMDemo {
public static void main(String[] args) throws InterruptedException {
// 最大記憶體
long maxMemory = Runtime.getRuntime().maxMemory();
System.out.println("最大堆記憶體為: "+format(maxMemory)+"MB");
// 已經使用記憶體
long totalMemory = Runtime.getRuntime(). totalMemory();
System.out.println("已使用堆記憶體: "+format(totalMemory)+"MB");
// 當前剩餘記憶體
long freeMemory = Runtime.getRuntime().freeMemory();
System.out.println("剩餘堆記憶體為: "+format(freeMemory)+"MB");
byte[] b1 = new byte[4 * 1024 * 1024];
System. out.println("-----分配了4m堆記憶體-----");
// 當前剩餘記憶體
long freeMemory2 = Runtime.getRuntime().freeMemory();
System.out.println("剩餘堆記憶體為: "+format(freeMemory2)+"MB");
}
/**
* 將堆記憶體單位格式化成 MB
*/
static private String format(long maxMemory) {
float num = (float) maxMemory / (1024 * 1024);
DecimalFormat df = new DecimalFormat("0.00");// 格式化小數
String s = df.format(num);// 返回的是String型別
return s;
}
}
1.2 配置引數,列印jvm資訊: 右鍵 --> Run As --> Run Configurations… --> Arguments --> VM arguments 輸入配置資訊 -XX:+PrintGCDetails -XX:+UseSerialGC
1.3 執行java程式碼, 檢視jvm資訊
// 這些資訊與電腦配置引數有關,我們的具體資料可能不一樣,但是記憶體模型資料比例是一樣的
最大堆記憶體為: 1890.81MB // 大約1900MB
已使用堆記憶體: 119.88MB // 已用120MB
剩餘堆記憶體為: 117.89MB // 剩餘117MB
-----分配了4m堆記憶體-----
剩餘堆記憶體為: 113.89MB // 用了4mb後還剩113MB
Heap
def new generation total 38080K, used 6805K [0x0000000085c00000, 0x0000000088550000, 0x00000000ae800000)
// 可以看出新生代中預設 eden區 from區 to區比例為 33856:4224:4224 即 8:1:1
eden space 33856K, 20% used [0x0000000085c00000, 0x00000000862a57a8, 0x0000000087d10000)
from space 4224K, 0% used [0x0000000087d10000, 0x0000000087d10000, 0x0000000088130000)
to space 4224K, 0% used [0x0000000088130000, 0x0000000088130000, 0x0000000088550000)
// 老年代和新生代預設比例為 84672:(33856+4224+4224) 即2:1
tenured generation total 84672K, used 0K [0x00000000ae800000, 0x00000000b3ab0000, 0x0000000100000000)
the space 84672K, 0% used [0x00000000ae800000, 0x00000000ae800000, 0x00000000ae800200, 0x00000000b3ab0000)
// 元空間
Metaspace used 3652K, capacity 4600K, committed 4864K, reserved 1056768K
class space used 410K, capacity 428K, committed 512K, reserved 1048576K
調優總結:
1.將初始的堆大小與最大堆大小相等,來垃圾回收次數從而提高效率。
2.根據自身伺服器效能,將堆記憶體最大化,以此提高吞吐量。
3.將新生代或老年代的比例設定為1/2 或者 1/3,讓GC儘量去新生代去回收。
4.合理的使用並行收集器。
在windows中設定JVM引數
修改 tomcat/bin/catalina.bat 檔案
set JAVA_OPTS=-Dfile.encoding=UTF-8 -server -XX:+PrintGCDetails -Xms900m -Xmx900m -XX:SurvivorRatio=8 -XX:NewRatio=2 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:ParallelGCThreads=8 -XX:MaxMetaspaceSize=128m
在linux中設定JVM引數
修改 tomcat/bin/catalina.sh 檔案
JAVA_OPTS="-Dfile.encoding=UTF-8 -server
-XX:+PrintGCDetails
-Xms1000m -Xmx1000m
-XX:SurvivorRatio=8 -XX:NewRatio=2
-XX:MaxMetaspaceSize=200m
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseParallelGC -XX:+UseParallelOldGC
-XX:ParallelGCThreads=8"
什麼是記憶體洩漏及如何避免
記憶體洩漏即:物件可達但不可用,程式不需要用某個物件,但另一個正在使用的物件卻持有它的引用,導致無法回收停留在堆內中。
防止記憶體洩露:
1.儘早釋放無用物件的引用, 將不需要使用的物件設定為null,暗示垃圾收集器來收集該物件,防止發生記憶體洩漏。
2.程式進行字串處理時,儘量避免使用String,而應該使用StringBuffer。
3.儘量少用靜態變數
4.儘量運用物件池技術以提高系統性能
Jvm常見異常處理方案
記憶體洩漏: java.lang.OutOfMemory Error:Java heap space
解決思路: 1. 先檢視是不是記憶體洩漏,如果是通過GC Root的路徑來排查
2. 檢視堆記憶體是否有物件沒釋放。
3. 加大實體記憶體 –Xms, -Xmx,最好-Xms = -Xmx,減少記憶體擴充套件的開銷。
控制引數:
-Xms (starting 堆的起始大小)
-Xmx (max 堆的最大大小)
-Xmn (new 堆的新生代大小)
記憶體溢位: java.OutOfMemory Error:PermGen space
解決思路:
增加引數:
-XX:PrintGCDetails,
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
執行緒請求的棧深度大於虛擬機器所允許的最大深度:java.lang.StackOverflow Error
解決思路: 可以將棧的深度,理解為陣列的長度。
控制引數:
1. 加大-Xss(每個執行緒的堆疊大小)引數。
2. 更換64位虛擬機器。
3. 減少執行緒。
4. 減少最大堆(Xmx)。
java.lang.OutOfMemory Error,有allocate、Native字樣
解決思路: 加大本地記憶體-MaxDirectMemorySize如不指定則與-Xmx一致。
以centos6.8 ,100G的記憶體 jdk1.7為例,參考配置如下:
JAVA_OPTS="-Xms8g -Xmx8g
-XX:ParallelGCThreads=8
-XX:PermSize=2g
-XX:MaxPermSize=4g
-Xss512k -Xmn6g
-XX:-DisableExplicitGC
-XX:+UseCompressedOops
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled"
CATALINA_OPTS="-Xms8g -Xmx8g
-XX:ParallelGCThreads=8
-XX:PermSize=2g
-XX:MaxPermSize=4g
-Xss512k -Xmn6g
-XX:-DisableExplicitGC
-XX:+UseCompressedOops
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled"