前置程序启动
事先启动一个web应用程序,用jps查看其进程id,接着用各种jdk自带命令优化应用
Jmap
此命令可以用来查看内存信息,实例个数以及占用内存大小
jmap -histo 14660 #查看历史生成的实例 jmap -histo:live 14660 #查看当前存活的实例,执行过程中可能会触发一次full gc
打开log.txt,文件内容如下:
* num:序号
* instances:实例数量
* bytes:占用空间大小
* class name:类名称,[C is a char[],[S is a short[],[I is a int[],[B is a byte[],[[I is a int[][]
堆信息
堆内存dump
jmap -dump:format=b,file=eureka.hprof 14660
也可以设置内存溢出自动导出dump文件(内存很大的时候,可能会导不出来),参数设置:
1.-XX:+HeapDumpOnOutOfMemoryError
2.-XX:HeapDumpPath=./ (路径)
示例代码:
1 public class OOMTest { 2 3 public static List<Object> list = new ArrayList<>(); 4 5 // JVM设置 6 // -Xms10M -Xmx10M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:jvm.dump 7 public static void main(String[] args) { 8 List<Object> list = new ArrayList<>(); 9 int i = 0; 10 int j = 0; 11 while (true) { 12 list.add(new User(i++, UUID.randomUUID().toString())); 13 new User(j--, UUID.randomUUID().toString()); 14 } 15 } 16 }
Jstack
用jstack加进程id查找死锁,见如下示例
1 public class DeadLockTest { 2 3 private static Object lock1 = new Object(); 4 private static Object lock2 = new Object(); 5 6 public static void main(String[] args) { 7 new Thread(() -> { 8 synchronized (lock1) { 9 try { 10 System.out.println("thread1 begin"); 11 Thread.sleep(5000); 12 } catch (InterruptedException e) { 13 } 14 synchronized (lock2) { 15 System.out.println("thread1 end"); 16 } 17 } 18 }).start(); 19 20 new Thread(() -> { 21 synchronized (lock2) { 22 try { 23 System.out.println("thread2 begin"); 24 Thread.sleep(5000); 25 } catch (InterruptedException e) { 26 } 27 synchronized (lock1) { 28 System.out.println("thread2 end"); 29 } 30 } 31 }).start(); 32 33 System.out.println("main thread end"); 34 } 35 }
还可以用jvisualvm自动检测死锁
远程连接jvisualvm
启动普通的jar程序JMX端口配置:
java -Dcom.sun.management.jmxremote.port=8888 -Djava.rmi.server.hostname=192.168.65.60 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -jar microservice-eureka-server.jar
PS:
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=8888 -Djava.rmi.server.hostname=192.168.50.60 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
连接时确认下端口是否通畅,可以临时关闭防火墙
systemctl stop firewalld #临时关闭防火墙
Jstack找出占用cpu最高的线程堆栈信息
1 /** 2 * 运行此代码,cpu会飙高 3 */ 4 public class Math { 5 6 public static final int initData = 666; 7 public static User user = new User(); 8 9 public int compute() { //一个方法对应一块栈帧内存区域 10 int a = 1; 11 int b = 2; 12 int c = (a + b) * 10; 13 return c; 14 } 15 16 public static void main(String[] args) { 17 Math math = new Math(); 18 while (true){ 19 math.compute(); 20 } 21 } 22 }
1.使用命令top -p ,显示你的java进程的内存情况,pid是你的java进程号,比如19663
2.按H,获取每个线程的内存情况
Jinfo
查看正在运行的Java应用程序的扩展参数
查看jvm参数
查看Java系统参数
Jstat
垃圾回收统计
* S0C:第一个幸存区的大小,单位KB
* S1C:第二个幸存区的大小
* S0U:第一个幸存区的使用大小
* S1U:第二个幸存区的使用大小
* EC:伊甸园区的大小
* EU:伊甸园区的使用大小
* OC:老年代大小
* OU:老年代使用大小
* MC:方法区大小(元空间)
* MU:方法区使用大小
* CCSC:压缩类空间大小
* CCSU:压缩类空间使用大小
* YGC:年轻代垃圾回收次数
* YGCT:年轻代垃圾回收消耗时间,单位s
* FGC:老年代垃圾回收次数
* FGCT:老年代垃圾回收消耗时间,单位s
* GCT:垃圾回收消耗总时间,单位s
堆内存统计
* NGCMN:新生代最小容量
* NGCMX:新生代最大容量
* NGC:当前新生代容量
* S0C:第一个幸存区大小
* S1C:第二个幸存区的大小
* EC:伊甸园区的大小
* OGCMN:老年代最小容量
* OGCMX:老年代最大容量
* OGC:当前老年代大小
* OC:当前老年代大小
* MCMN:最小元数据容量
* MCMX:最大元数据容量
* MC:当前元数据空间大小
* CCSMN:最小压缩类空间大小
* CCSMX:最大压缩类空间大小
* CCSC:当前压缩类空间大小
* YGC:年轻代gc次数
* FGC:老年代GC次数
新生代垃圾回收统计
* S0C:第一个幸存区的大小
* S1C:第二个幸存区的大小
* S0U:第一个幸存区的使用大小
* S1U:第二个幸存区的使用大小
* TT:对象在新生代存活的次数
* MTT:对象在新生代存活的最大次数
* DSS:期望的幸存区大小
* EC:伊甸园区的大小
* EU:伊甸园区的使用大小
* YGC:年轻代垃圾回收次数
* YGCT:年轻代垃圾回收消耗时间
新生代内存统计
* NGCMN:新生代最小容量
* NGCMX:新生代最大容量
* NGC:当前新生代容量
* S0CMX:最大幸存1区大小
* S0C:当前幸存1区大小
* S1CMX:最大幸存2区大小
* S1C:当前幸存2区大小
* ECMX:最大伊甸园区大小
* EC:当前伊甸园区大小
* YGC:年轻代垃圾回收次数
* FGC:老年代回收次数
老年代垃圾回收统计
* MC:方法区大小
* MU:方法区使用大小
* CCSC:压缩类空间大小
* CCSU:压缩类空间使用大小
* OC:老年代大小
* OU:老年代使用大小
* YGC:年轻代垃圾回收次数
* FGC:老年代垃圾回收次数
* FGCT:老年代垃圾回收消耗时间
* GCT:垃圾回收消耗总时间
老年代内存统计
* OGCMN:老年代最小容量
* OGCMX:老年代最大容量
* OGC:当前老年代大小
* OC:老年代大小
* YGC:年轻代垃圾回收次数
* FGC:老年代垃圾回收次数
* FGCT:老年代垃圾回收消耗时间
* GCT:垃圾回收消耗总时间
元数据空间统计
* MCMN:最小元数据容量
* MCMX:最大元数据容量
* MC:当前元数据空间大小
* CCSMN:最小压缩类空间大小
* CCSMX:最大压缩类空间大小
* CCSC:当前压缩类空间大小
* YGC:年轻代垃圾回收次数
* FGC:老年代垃圾回收次数
* FGCT:老年代垃圾回收消耗时间
* GCT:垃圾回收消耗总时间
* S0:幸存1区当前使用比例
* S1:幸存2区当前使用比例
* E:伊甸园区使用比例
* O:老年代使用比例
* M:元数据区使用比例
* CCS:压缩使用比例
* YGC:年轻代垃圾回收次数
* FGC:老年代垃圾回收次数
* FGCT:老年代垃圾回收消耗时间
* GCT:垃圾回收消耗总时间
JVM运行情况预估
年轻代对象增长的速率
1)机器配置:2核4G
2)JVM内存大小:2G
3)系统运行时间:7天
4)期间发生的Full GC次数和耗时:500多次,200多秒
5)期间发生的Young GC次数和耗时:1万多次,500多秒
-Xms1536M -Xmx1536M -Xmn512M -Xss256K -XX:SurvivorRatio=6 -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly
jstat -gc 13456 2000 10000
-Xms1536M -Xmx1536M -Xmn1024M -Xss256K -XX:SurvivorRatio=6 -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=92 -XX:+UseCMSInitiatingOccupancyOnly
我们可以推测下full gc比minor gc还多的原因有哪些?
1 @RestController 2 public class IndexController { 3 4 @RequestMapping("/user/process") 5 public String processUserData() throws InterruptedException { 6 ArrayList<User> users = queryUsers(); 7 8 for (User user: users) { 9 //TODO 业务处理 10 System.out.println("user:" + user.toString()); 11 } 12 return "end"; 13 } 14 15 /** 16 * 模拟批量查询用户场景 17 * @return 18 */ 19 private ArrayList<User> queryUsers() { 20 ArrayList<User> users = new ArrayList<>(); 21 for (int i = 0; i < 5000; i++) { 22 users.add(new User(i,"zhuge")); 23 } 24 return users; 25 } 26 }