JVM调优工具

  • 前置启动程序
    • JPS
    • JMAP
    • 查看堆信息
    • 堆内存dump
      • 用jvisualvm命令工具导入该dump文件分析
    • Jstack
    • 用jvisualvm自动检测死锁
    • jstack找出占用cpu最高的线程堆栈信息
    • Jinfo
      • 查看JVM的参数
      • 查看java系统参数
    • Jstat
      • 垃圾回收统计
      • 堆内存统计
      • 新生代垃圾回收统计
      • 新生代内存统计
      • 老年代垃圾回收统计
      • 老年代内存统计
      • 元数据空间统计
    • JVM运行情况预估
      • 年轻代对象增长速率
      • Young GC的触发频率和每次耗时
      • 每次Young GC后有多少对象存活和进入老年代
      • Full GC的触发频率和每次耗时
    • 系统频繁Full GC导致系统卡顿是怎么回事
      • 内存泄露到底是怎么回事

前置启动程序

事先启动一个Web应用程序,用JPS查看其进程ID,接着用各种JDK自带命令优化引用;

JPS

JMAP

可以用来查看内存信息,实例个数以及占用内存大小
用法1:JMAP -histo Java进程ID
控制台显示进程里内存的使用情况;
用户2:JMAP -histo Java进程ID > 文件路径
将JVM的内存使用情况输出到某个文件中;

运行Java程序,将内存使用情况输入到log.txt中;
JMAP -histo 14660 > ./log.txt
内容如下:

  • num:序号
  • instances:实力数量
  • bytes:占用空间大小
  • class name:类名称;[C 为char[] ,[B 为byte[] ,[S 为short[], [[I 为int[][];

查看堆信息

  • JMAP -heap 进程号
  • JMAP -heap 进程号 > 文件路径
  1. 查看所有进程
  2. 查看堆信息
Attaching to process ID 11120, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.144-b01using thread-local object allocation.
Parallel GC with 8 thread(s)
//堆参数的设置
Heap Configuration:MinHeapFreeRatio         = 0MaxHeapFreeRatio         = 100//最大堆容量MaxHeapSize              = 2118123520 (2020.0MB)//年轻代大小NewSize                   = 44564480 (42.5MB)MaxNewSize               = 705691648 (673.0MB)//老年代大小OldSize                  = 89653248 (85.5MB)NewRatio                 = 2SurvivorRatio            = 8MetaspaceSize            = 21807104 (20.796875MB)CompressedClassSpaceSize = 1073741824 (1024.0MB)MaxMetaspaceSize         = 17592186044415 MBG1HeapRegionSize         = 0 (0.0MB)Heap Usage:
PS Young Generation
//Eden区使用了32M
Eden Space:capacity = 34078720 (32.5MB)used     = 10127792 (9.658615112304688MB)free     = 23950928 (22.841384887695312MB)29.71881573016827% used
From Space:
//From区使用0Mcapacity = 5242880 (5.0MB)used     = 0 (0.0MB)free     = 5242880 (5.0MB)0.0% used
//TO区使用0M
To Space:capacity = 5242880 (5.0MB)used     = 0 (0.0MB)free     = 5242880 (5.0MB)0.0% used
PS Old Generation
//old区使用0Mcapacity = 89653248 (85.5MB)used     = 0 (0.0MB)free     = 89653248 (85.5MB)0.0% used1816 interned Strings occupying 161432 bytes.

堆内存dump

JMAP -dump:format=b, file=文件名 进程号

示例:
也可以设置内存溢出自动导出dump文件(内存很大的时候,可能会导不出来)

  1. -XX:+HeapDumpOnOutOfMemoryError
  2. -XX:HeapDumpPath=./ (路径)

示例代码:

package com.jvm.jvm1;import java.util.ArrayList;
import java.util.List;
import java.util.UUID;public class OOMTest {public static List<Object> list=new ArrayList<>();public static void main(String[] args) {List<Object> list=new ArrayList<>();int i=0;int j=0;while(true){list.add(new User(i++, UUID.randomUUID().toString()));new User(j--,UUID.randomUUID().toString());}}
}

运行:

用jvisualvm命令工具导入该dump文件分析

1)概要信息

2)类信息

Jstack

查看死锁;
jstack 进程ID

示例代码:

package com.jvm.jvm1;public class DeadLockTest {private static Object lock1 = new Object();private static Object lock2 = new Object();public static void main(String[] args) {new Thread(()->{synchronized (lock1){try {System.out.println("Abegin");Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock2){System.out.println("Aend");}}}).start();new Thread(()->{synchronized (lock2){try {System.out.println("Bbegin");Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock1){System.out.println("Bend");}}}).start();}
}

检测到了死锁

  • Thread-1:线程名
  • prio=5 优先级=5
  • tid=0x000000001aa0d800 线程ID
  • nid=0x1a70 线程对应的本地线程标识
  • java.lang.Thread.State:BLOCKED 线程状态

用jvisualvm自动检测死锁

jstack找出占用cpu最高的线程堆栈信息

示例代码

package com.jvm.jvm1;public class Math {public static final int initData=555;public static User user=new User();public static int count=0;public static int compute(){User user2=new User();int a=1;int b=2;int c= (a+b) *10;int d=(a+b)*20;return c;}public static void main(String[] args){while(true){compute();}}
}
  1. Linux 使用命令top -p ,显示你的java进程的内存情况,pid是你的java进程号 比如19663
  2. 按住H,获取每个线程的内存情况
  3. 找到内存和CPU占用最高的线程TID,比如19664
  4. 转为十六进制0x4cd0 ,为线程ID的十六进制表示
  5. 执行jstack 19663 | grep -A 10 4cd0
    得到线程堆栈信息中4cd0这个线程所在行的后面10行,从堆栈中可以发现导致CPU飙高的调用方法;
  6. 查看对应堆栈信息找出可能存在的问题代码

Jinfo

查看正在运行的Java应用程序的扩展参数

查看JVM的参数

jinfo -flags 进程名


E:\新建文件夹 (4)\jvm>jinfo -flags 1856
Attaching to process ID 1856, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.144-b01
Non-default VM flags: -XX:CICompilerCount=4 -XX:InitialHeapSize=1258291200 -XX:MaxHeapSize=1258291200 -XX:MaxNewSize=419430400 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=419430400 -XX:OldSize=838860800 -XX:+UseCompressedClassPoi
nters -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
Command line:  -Xmx1200m -Xms1200m -javaagent:E:\IDEA2017\IntelliJ IDEA 2017.3.5\lib\idea_rt.jar=41998:E:\IDEA2017\IntelliJ IDEA 2017.3.5\bin -Dfile.encoding=UTF-8

查看java系统参数

jinfo -sysprops 进程号

Jstat

查看堆内存各部分的使用情况,以及加载类的数量。
jstat [-命令选项] [vmid] [间隔时间(毫秒)] [查询次数]

垃圾回收统计

jstat -gc 进程ID:评估程序内存使用以及GC压力情况

E:\新建文件夹 (4)\jvm>jstat -gc 1856S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
51200.0 51200.0  0.0    0.0   307200.0 24576.1   819200.0     0.0     4480.0 770.4  384.0   75.9       0    0.000   0      0.000    0.000

  • S0C :第一个幸存者区的大小,单位为KB
  • S1C :第二个幸存者区大小
  • S0U: 第一个幸存者区使用大小
  • S1U:第二个幸存者区使用大小
  • EC:Eden区大小
  • EU:Eden区使用大小
  • OC:老年代大小
  • OU:老年代使用大小
  • MC:元空间大小
  • MU:元空间使用大小
  • CCSC:压缩类空间大小
  • CCSU:压缩类空间使用大小
  • YGC:年轻代垃圾回收次数、
  • YGCT:年轻代垃圾回收消耗时间,单位s
  • FUC:老年代垃圾回收次数
  • FUCT:老年代垃圾回收消耗时间,单位s
  • GCT:垃圾回收总耗时间,单位s

堆内存统计

jstat -gccapacity 进程编号

E:\新建文件夹 (4)\jvm>jstat -gccapacity 15172NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC
409600.0 409600.0 409600.0 51200.0 51200.0 307200.0   819200.0   819200.0   819200.0   819200.0      0.0 1056768.0   4480.0      0.0 1048576.0    384.0      0     0

  • NGCMN:新生代最小容量
  • NGCMX:新生代最大容量
  • NGC:当前新生代容量
  • S0C:第一个幸存者区大小
  • S1C:第二个幸存者去大小
  • EC:Eden区大小
  • OGCMN:老年代最小容量
  • OGCMX:老年代最大容量
  • OGC:当前老年代大小
  • MCMN:元空间最小容量
  • MCMX:元空间最大容量
  • MC:元空间当前大小
  • CCSMN:最小压缩类空间大小
  • CCSMX:最大压缩类空间大小
  • CCSC:当前压缩类空间大小
  • YGC:年轻代GC次数
  • FGC:老年代GC次数

新生代垃圾回收统计

jstat -gcnew 进程ID

  • S0C :第一个幸存者区的大小,单位为KB
  • S1C :第二个幸存者区大小
  • S0U: 第一个幸存者区使用大小
  • S1U:第二个幸存者区使用大小
  • TT:对象在新生代存活的次数
  • MTT:对象在新生代存活的最大次数
  • DSS:期望的Eden大小
  • EC:Eden区大小
  • EU:Eden区使用大小
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间

新生代内存统计

jstat -gcnewcapacity 进程编号

E:\新建文件夹 (4)\jvm>jstat -gcnewcapacity 15172NGCMN      NGCMX       NGC      S0CMX     S0C     S1CMX     S1C       ECMX        EC      YGC   FGC409600.0   409600.0   409600.0 136192.0  51200.0 136192.0  51200.0   408576.0   307200.0     0     0
  • NGCMN:新生代最小容量
  • NGCMX:新生代最大容量
  • NGC:当前新生代容量
  • S0CMX:最大幸存1区大小
  • S0C:当前幸存1区大小
  • S1CMX:最大幸存2区大小
  • S1C:当前幸存2区大小
  • ECMX:最大伊甸园区大小
  • EC:当前伊甸园区大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代回收次数

老年代垃圾回收统计

jstat -gcold 进程ID

E:\新建文件夹 (4)\jvm>jstat -gcold 12956MC       MU      CCSC     CCSU       OC          OU       YGC    FGC    FGCT     GCT17024.0  16363.6   2176.0   1957.2     87552.0       119.1      2     0    0.000    0.009
  • 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:垃圾回收消耗总时间

JVM运行情况预估

用jstat -gc -pid命令可以计算出一些关键数据,有了这些数据就可以采用之前介绍过的优化思路,先给自己的系统设置一些初始性的JVM参数,比如堆内存大小,年轻代大小,Eden与S区的比例,老年代大小,大对象阈值,元空间大小等

年轻代对象增长速率

可以执行命令jstat -gc pid 1000 10 (每一秒执行一次命令,共10次),通过观察EU来估算每秒Eden去大概新增多少对象,如果系统负载不高,可以换成10秒,或者1分钟来观察整体情况。一般系统会有高峰期和日常器,需要在不同时间分别估算不同对象增长速率;

Young GC的触发频率和每次耗时

知道年轻代对象增长速率我们就能推根据eden区的大小推算出Young GC大概多久触发一次,Young GC的平均耗时可以通过 YGCT/YGC 公式算出,根据结果我们大概就能知道系统大概多久会因为Young GC的执行而卡顿多久

每次Young GC后有多少对象存活和进入老年代

这个因为之前已经大概知道Young GC的频率,假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden, survivor和老年代使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次 Young GC后存活的对象,同时还可以看出每次Young GC后进去老年代大概多少对象,从而可以推算出老年代对象增长速率。

Full GC的触发频率和每次耗时

知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。

优化思路其实简单来说就是尽量让每次Young GC后的存活对象小于Survivor区域的50%,都留存在年轻代里。尽量别让对象进入老年 代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响

系统频繁Full GC导致系统卡顿是怎么回事

  • 机器配置:2核4G
  • JVM内存大小:2G
  • 系统运行时间:7天
  • 期间发生的Full GC次数和耗时:500多次,200多秒
  • 期间发生的Young GC次数和耗时:1万多次,500多秒

大致算下来每天会发生70多次Full GC,平均每小时3次,每次Full GC在400毫秒左右;
每天会发生1000多次Young GC,每分钟会发生1次,每次Young GC在50毫秒左右。

JVM参数设置如下:

‐Xms1536M ‐Xmx1536M ‐Xmn512M ‐Xss256K ‐XX:SurvivorRatio=6 ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M
XX:+UseParNewGC ‐XX:+UseConcMarkSweepGC ‐XX:CMSInitiatingOccupancyFraction=75 ‐XX:+UseCMSInitiatingOccupancyOnly


大家可以结合对象挪动到老年代那些规则推理下我们这个程序可能存在的一些问题
经过分析感觉可能会由于对象动态年龄判断机制导致full gc较为频繁
为了给大家看效果,我模拟了一个示例程序(见课程对应工程代码:jvm-full-gc),打印了jstat的结果如下:

jstat ‐gc 13456 2000 10000


对于对象动态年龄判断机制导致的full gc较为频繁可以先试着优化下JVM参数,把年轻代适当调大点

‐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的次数还多了

我们可以推测下full gc比minor gc还多的原因有哪些?
1、元空间不够导致的多余full gc
2、显示调用System.gc()造成多余的full gc,这种一般线上尽量通过­XX:+DisableExplicitGC参数禁用,如果加上了这个JVM启动参数,那 么代码中调用System.gc()没有任何效果
3、老年代空间分配担保机制

最快速度分析完这些我们推测的原因以及优化后,我们发现young gc和full gc依然很频繁了,而且看到有大量的对象频繁的被挪动到老年 代,这种情况我们可以借助jmap命令大概看下是什么对象

查到了有大量User对象产生,这个可能是问题所在,但不确定,还必须找到对应的代码确认,如何去找对应的代码了?
1、代码里全文搜索生成User对象的地方(适合只有少数几处地方的情况)
2、如果生成User对象的地方太多,无法定位具体代码,我们可以同时分析下占用cpu较高的线程,一般有大量对象不断产生,对应的方法 代码肯定会被频繁调用,占用的cpu必然较高 可以用上面讲过的jstack或jvisualvm来定位cpu使用较高的代码,最终定位到的代码如下:

import java.util.ArrayList; @RestController public class IndexController { @RequestMapping("/user/process") public String processUserData() throws InterruptedException { ArrayList<User> users = queryUsers(); for (User user: users) { //TODO 业务处理 System.out.println("user:" + user.toString()); } return "end"; }private ArrayList<User> queryUsers() {ArrayList<User> users = new ArrayList<>();for (int i = 0; i < 5000; i++) { users.add(new User(i,"zhuge")); } return users;  }}

同时,java的代码也是需要优化的,一次查询出500M的对象出来,明显不合适,要根据之前说的各种原则尽量优化到合适的值,尽量消 除这种朝生夕死的对象导致的full gc

内存泄露到底是怎么回事

再给大家讲一种情况,一般电商架构可能会使用多级缓存架构,就是redis加上JVM级缓存,大多数同学可能为了图方便对于JVM级缓存就 简单使用一个hashmap,于是不断往里面放缓存数据,但是很少考虑这个map的容量问题,结果这个缓存map越来越大,一直占用着老 年代的很多空间,时间长了就会导致full gc非常频繁,这就是一种内存泄漏,对于一些老旧数据没有及时清理导致一直占用着宝贵的内存 资源,时间长了除了导致full gc,还有可能导致OOM。 这种情况完全可以考虑采用一些成熟的JVM级缓存框架来解决,比如ehcache等自带一些LRU数据淘汰算法的框架来作为JVM级的缓存。

JVM调优与工具命令相关推荐

  1. JVM调优系列--Java命令选项(参数)--大全/详解/常用

    原文网址:JVM调优系列--Java命令选项(参数)--大全/详解/常用_IT利刃出鞘的博客-CSDN博客 简介 说明        本文介绍Java的java命令用法,包括:常用用法.选项大全. J ...

  2. 关于JVM调优的工具及JVM 常见调优参数

    六个命令行排查工具 我们一般使用 JDK 自带的 6 个命令行工具来排查JVM.它们分别是:jps.jstat.jinfo.jmap.jhat 和 jstack,它们都位于 JDK 的 bin 目录下 ...

  3. JVM 调优实战--jhat命令使用详解

    jhat也是jdk内置的工具之一.主要是用来分析java堆的命令,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言. 使用jmap等方法生成java的堆文件后,使 ...

  4. JVM 调优实战--常用命令参数及PS收集器的GC日志格式

    目录 了解JVM常用命令行参数 Parallel Scavenge(PS)的GC日志格式 了解JVM常用命令行参数 JVM的命令行参数参考:https://docs.oracle.com/javase ...

  5. JVM从跨平台到跨专业 Ⅲ --编译期处理、类加载、JVM调优

    文章目录 编译期处理 默认构造器 自动拆装箱 泛型集合取值 可变参数 foreach 循环 switch 字符串 switch 枚举 枚举类 try-with-resources 方法重写时的桥接方法 ...

  6. Jvm 系列(七):Jvm 调优-工具篇

    工具做为图形化界面来展示更能直观的发现问题,另一方面一些耗费性能的分析(dump文件分析)一般也不会在生产直接分析,往往dump下来的文件达1G左右,人工分析效率较低,因此利用工具来分析jvm相关问题 ...

  7. jvm系列(四):jvm调优-命令大全(jps jstat jmap jhat jstack jinfo)

    2019独角兽企业重金招聘Python工程师标准>>> 文章同步发布于github博客地址,阅读效果更佳,欢迎品尝 运用jvm自带的命令可以方便的在生产监控和打印堆栈的日志信息帮忙我 ...

  8. jvm系列(七):jvm调优-工具篇

    16年的时候花了一些时间整理了一些关于jvm的介绍文章,到现在回顾起来还是一些还没有补充全面,其中就包括如何利用工具来监控调优前后的性能变化.工具做为图形化界面来展示更能直观的发现问题,另一方面一些耗 ...

  9. JVM调优:运行参数,内存模型,mat、jps、jstat、jmap、jstack、jvisualvm工具的使用

    JVM调优 - 工具篇 作者:张学亮 讲解内容 了解下我们为什么要学习JVM优化 掌握jvm的运行参数以及参数的设置 掌握jvm的内存模型(堆内存) 掌握jamp命令的使用以及通过MAT工具进行分析 ...

最新文章

  1. Caused by: java.sql.BatchUpdateException
  2. postman断言测试脚本二 (对数据格式和内容匹配测试)
  3. linux mysql 实例详解_MySQL 多实例详解
  4. 透彻理解RPN: 从候选区域搜索到候选区域提取网络
  5. Python的reshape(-1,1)
  6. 私有属性和方法-伪私有属性和方法
  7. 反演控制 matlab,基于matlab的反演程序
  8. 数据缺失,如何智能修复?第一名方案源码分享
  9. c语言文件归档,Go语言tar归档文件的读写操作
  10. Linux命令详解词典
  11. 优化你简历的8个技巧
  12. 不会编程却想做APP ? 让Power Apps 来帮你
  13. 记一次酷狗音乐API的获取,感兴趣的可以自己封装开发自己的音乐播放器
  14. 第二章——Swift语言
  15. CPU 的工作原理以及为什么Apple Silicon M1 比 Intel i9 快?
  16. 解决浏览器 Microsoft Edge主页被hao123恶意篡改
  17. 服务器项目迁移本地,云服务器迁移本地
  18. hexo自定义域名以及解析
  19. 电脑磁盘空间莫名其妙满了
  20. C# Remoting的基础介绍

热门文章

  1. PIL.JpegImagePlugin.JpegImageFile与numpy.ndarray格式转换
  2. python-最后-高级技巧
  3. 使用Neo4j和简单分词算法实现菜品推荐系统
  4. 【JFreeChart】自定义蜘蛛网图生成带刻度三角雷达图 自定义文字风格 背景色
  5. PySpark机器学习 MLlib
  6. EMQ-X 消息存储到数据库的四种方法
  7. 电商的下半场,佰达慧兴述淘宝不再想只做一个“货架电商”
  8. 配置 SSH 双因素认证
  9. JavaDay05.练习4.体彩7位数摇奖+福彩大乐透摇奖
  10. 3dmax使用MAXScript调整对象的轴位置