JVM引數型別
標準引數
基本上不變,相對比較穩定
- -help
- -server 、-client
- -version 、-showversion
- -cp 、-classpath
非標準化引數
在部分JVM裡面會有變化,但是變化小
- -Xint:解釋執行
- -Xcomp:第一次使用就編譯成原生程式碼
- -Xmixed:混合模式,JVM自己來決定是否編譯成原生程式碼
XX引數
XX引數是非標準化引數、相對不穩定、主要用於JVM調優和Debug,分為2大類:
- Boolean型別
格式:-XX:[+-] 表示(+)啟用或者禁用(-)name屬性
比如:-XX:+UseConMarkSweepGC
-XX:UseG1GC - 非Boolean型別
格式:-XX: = 表示name的屬性值是value
比如:-XX:MaxGCPauseMillis = 200
-XX:GCTimeRatio = 19
-Xmx(最大JVM記憶體)-Xms(最小JVM內在)
它不是X引數,而是XX引數
-Xms等價於-XX:InitialHeapSize
-Xmx等價於-XX:MaxHeapSize
在linux中檢視java程式記憶體大小 jinfo -flag MaxHeapSize [程式ID]
如何檢視JVM執行時引數
- -XX:+PrintFlagsInitial 檢視初始時的一個值
- -XX:+PrintFlagsFinal 檢視最終的一個值
- -XX:+UnlockExperimentalVMOptions 解鎖實驗引數
- -XX:+UnlockDiagnosticVMOptions 解鎖診斷引數
- -XX:+PrintCommandLineFlags 列印命令列引數
例項:java -XX:+PrintFlagsFinal -version
:=被使用者或者JVM修改後的值
jps
jps 命令類似與 linux 的 ps 命令,但是它只列出系統中所有的 Java 應用程式。 通過 jps 命令可以方便地檢視 Java 程式的啟動類、傳入引數和 Java 虛擬機器引數等資訊。 具體參考jvm 效能調優工具之jps
jinfo
檢視一個JVM正在執行的引數值
jinfo舉例- 檢視最大記憶體
jinfo -flag MaxHeapSize [程式ID]
- 檢視垃圾回收器
jinfo -flag UseConcMarkSweepGC/UseG1GC/UseParallelGC [程式ID]
使用jstat檢視jvm統計資訊
類載入資訊
部分options: -class, -compiler,-gc, -printcompilation 更多可點此處檢視
垃圾收集資訊
部分options: -gc, -gcutil,-gccause, -gcnew, -gcold 更多可點此處檢視
-gc輸出結果引數說明:- S0C、S1C、S0U:S0和S1的總量與使用量
- EC、EU:Eden區總量和使用量
- OC、OU:Old區總量和使用量
- MC、MU:Metaspace區總量和使用量
- CCSC、CCSU:壓縮類空間總量和使用量
- YGC、YGCT:YoungGC的次數與時間
- FGC、FGCT:FullGC的次數與時間
- GCT:總的GC時間
JIT編譯資訊
部分options: -compiler, -printcompilation 更多可點此處檢視
jmap+MAT分析記憶體溢位
例項測試專案基於spring boot快速搭建
User.java
public class User{
private int id;
private String name;
# 構造方法
# get() and set()
}
複製程式碼
MemoryController.java
@RestController
public class MemoryController{
private List<User> userList = new ArrayList<User>();
/**
* 設定堆最大最小記憶體,方便快速除錯(-Xmx32M -Xms32M)
**/
@GetMapping("/heap") ##基於堆的記憶體溢位
public String heap(){
int i = 0;
while(true){
userList.add(new User(i++, UUID.randomUUID().toString()));
}
}
/**
* 設定非堆最大最小記憶體(-XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M)
**/
@GetMapping("/noheap") ## 非堆的記憶體溢位
public String noheap(){
while(true){ ##基於動態生成class測試
classList.addAll(Metaspace.createClasses());
}
}
}
複製程式碼
Metaspace.java
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/*
* 繼承ClassLoader是為了方便呼叫defineClass方法,因為該方法的定義為protected
* */
public class Metaspace extends ClassLoader {
## 類持有
List<Class<?>> classes = new ArrayList<Class<?>>();
## 迴圈1000w次生成1000w個不同的類。
for (int i = 0; i < 10000000; ++i) {
ClassWriter cw = new ClassWriter(0);
## 定義一個類名稱為Class{i},它的訪問域為public,父類為java.lang.Object,不實現任何介面
cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
## 定義建構函式<init>方法
MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>","()V", null, null);
## 第一個指令為載入this
mw.visitVarInsn(Opcodes.ALOAD, 0);
## 第二個指令為呼叫父類Object的建構函式
mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
## 第三條指令為return
mw.visitInsn(Opcodes.RETURN);
mw.visitMaxs(1, 1);
mw.visitEnd();
Metaspace test = new Metaspace();
byte[] code = cw.toByteArray();
## 定義類
Class<?> exampleClass = test.defineClass("Class" + i, code, 0, code.length);
classes.add(exampleClass);
}
}
複製程式碼
訪問路徑localhost:8080/heap 堆記憶體溢位圖示
訪問路徑localhost:8080/noheap 非堆記憶體溢位圖示匯出應用程式記憶體映像檔案
有2中方式可以匯出,分別是:記憶體溢位自動匯出、使用jmap命令手動匯出
- 記憶體溢位自動匯出——使用如下jvm引數選項
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./ ##要匯出的檔案路徑
複製程式碼
- 使用jmap命令手動匯出
部分options: -heap, -clstats, -dump:, -F
更多可點此處檢視
資料過大可能無法匯出。
下圖示例:
使用MAT分析定位錯誤
MAT安裝及使用教程
記憶體分析工具 MAT 的使用
MAT使用進階
jstack實戰執行緒異常
Java的堆疊跟蹤 - 為給定的程式或核心檔案或遠端除錯伺服器列印執行緒的堆疊跟蹤。
選項 | 說明 |
---|---|
-F | 當jstack[ -l] pid沒有響應時強制執行堆疊轉儲。 |
-l | 列印有關鎖的其他資訊,例如擁有的可擁有java.util.concurrent同步器列表 |
-m | 列印具有Java和本機C / C ++幀的混合模式堆疊跟蹤。 |
-H | 列印幫助資訊。 |
-help | 列印幫助資訊。 |
java執行緒的狀態
文獻參考地址
下表列出了使用Control + Break Handler進行執行緒轉儲的可能執行緒狀態。
執行緒狀態 | 描述 |
---|---|
NEW | 該主題尚未開始。 |
RUNNABLE | 執行緒正在JVM中執行。 |
BLOCKED | 執行緒被阻塞等待監視器鎖定。 |
WAITING | 執行緒無限期地等待另一個執行緒執行特定操作。 |
TIMED_WAITING | 執行緒正在等待另一個執行緒執行最多指定等待時間的操作。 |
TERMINATED | 執行緒已經退出。 |
執行緒狀態流轉圖
例項-CPU飆高
CpuController.java
import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CpuController {
/**
* 死迴圈
* */
@RequestMapping("/loop")
public List<Long> loop(){
String data = "{\"data\":[{\"partnerid\":]";
return getPartneridsFromJson(data);
}
private Object lock1 = new Object();
private Object lock2 = new Object();
/**
* 死鎖
* */
@RequestMapping("/deadlock")
public String deadlock(){
new Thread(()->{
synchronized(lock1) {
try {Thread.sleep(1000);}catch(Exception e) {}
synchronized(lock2) {
System.out.println("Thread1 over");
}
}
}) .start();
new Thread(()->{
synchronized(lock2) {
try {Thread.sleep(1000);}catch(Exception e) {}
synchronized(lock1) {
System.out.println("Thread2 over");
}
}
}) .start();
return "deadlock";
}
public static List<Long> getPartneridsFromJson(String data){
##{\"data\":[{\"partnerid\":982,\"count\":\"10000\",\"cityid\":\"11\"},{\"partnerid\":983,\"count\":\"10000\",\"cityid\":\"11\"},{\"partnerid\":984,\"count\":\"10000\",\"cityid\":\"11\"}]}
##上面是正常的資料
List<Long> list = new ArrayList<Long>(2);
if(data == null || data.length() <= 0){
return list;
}
int datapos = data.indexOf("data");
if(datapos < 0){
return list;
}
int leftBracket = data.indexOf("[",datapos);
int rightBracket= data.indexOf("]",datapos);
if(leftBracket < 0 || rightBracket < 0){
return list;
}
String partners = data.substring(leftBracket+1,rightBracket);
if(partners == null || partners.length() <= 0){
return list;
}
while(partners!=null && partners.length() > 0){
int idpos = partners.indexOf("partnerid");
if(idpos < 0){
break;
}
int colonpos = partners.indexOf(":",idpos);
int commapos = partners.indexOf(",",idpos);
if(colonpos < 0 || commapos < 0){
//partners = partners.substring(idpos+"partnerid".length()); #註釋該部分程式碼丟擲問題
continue;
}
String pid = partners.substring(colonpos+1,commapos);
if(pid == null || pid.length() <= 0){
//partners = partners.substring(idpos+"partnerid".length()); #註釋該部分程式碼丟擲問題
continue;
}
try{
list.add(Long.parseLong(pid));
}catch(Exception e){
//do nothing
}
partners = partners.substring(commapos);
}
return list;
}
}
複製程式碼
top命令查詢Linux cpu
匯出檔案jstack [程式ID] > [fileName]
對匯出後的檔案內容進行分析定位,可以參考
【JVM效能調優】jstack和執行緒dump分析
java運維 jstack dump日誌檔案詳解
輸出所有執行緒 top -p [程式ID] -H