基本上不變,相對比較穩定html
在部分JVM裏面會有變化,可是變化小java
XX參數是非標準化參數、相對不穩定、主要用於JVM調優和Debug,分爲2大類:linux
-Xmx(最大JVM內存)-Xms(最小JVM內在)
它不是X參數,而是XX參數
-Xms等價於-XX:InitialHeapSize
-Xmx等價於-XX:MaxHeapSize
在linux中查看java進程內存大小 jinfo -flag MaxHeapSize [進程ID]
web
實例:java -XX:+PrintFlagsFinal -version
spring
jps 命令相似與 linux 的 ps 命令,可是它只列出系統中全部的 Java 應用程序。 經過 jps 命令能夠方便地查看 Java 進程的啓動類、傳入參數和 Java 虛擬機參數等信息。 具體參考jvm 性能調優工具之jps tomcat
查看一個JVM正在運行的參數值 bash
jinfo -flag MaxHeapSize [進程ID]
jinfo -flag UseConcMarkSweepGC/UseG1GC/UseParallelGC [進程ID]
部分options: -class, -compiler,-gc, -printcompilation 更多可點此處查看 oracle
部分options: -gc, -gcutil,-gccause, -gcnew, -gcold 更多可點此處查看 app
部分options: -compiler, -printcompilation 更多可點此處查看
實例測試項目基於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 堆內存溢出圖示
有2中方式能夠導出,分別是:內存溢出自動導出、使用jmap命令手動導出
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./ ##要導出的文件路徑
複製代碼
MAT安裝及使用教程
內存分析工具 MAT 的使用
MAT使用進階
Java的堆棧跟蹤 - 爲給定的進程或核心文件或遠程調試服務器打印線程的堆棧跟蹤。
選項 | 說明 |
---|---|
-F | 當jstack[ -l] pid沒有響應時強制執行堆棧轉儲。 |
-l | 打印有關鎖的其餘信息,例如擁有的可擁有java.util.concurrent同步器列表 |
-m | 打印具備Java和本機C / C ++幀的混合模式堆棧跟蹤。 |
-H | 打印幫助信息。 |
-help | 打印幫助信息。 |
文獻參考地址
下表列出了使用Control + Break Handler進行線程轉儲的可能線程狀態。
線程狀態 | 描述 |
---|---|
NEW | 該主題還沒有開始。 |
RUNNABLE | 線程正在JVM中執行。 |
BLOCKED | 線程被阻塞等待監視器鎖定。 |
WAITING | 線程無限期地等待另外一個線程執行特定操做。 |
TIMED_WAITING | 線程正在等待另外一個線程執行最多指定等待時間的操做。 |
TERMINATED | 線程已經退出。 |
線程狀態流轉圖
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]
輸出全部線程 top -p [進程ID] -H
查看死鎖,循環
查看熱點方法執行時間
要選擇jdk版本對應插件中心的地址