基本上不变,相对比较稳定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版本对应插件中心的地址