JVM调优与工具命令

前置启动程序

事先启动一个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-b01

using thread-local object allocation.
Parallel GC with 8 thread(s)
//堆参数的设置
Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   //最大堆容量
   MaxHeapSize              = 2118123520 (2020.0MB)
   //年轻代大小
   NewSize    			    = 44564480 (42.5MB)
   MaxNewSize               = 705691648 (673.0MB)
   //老年代大小
   OldSize                  = 89653248 (85.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 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区使用0M
   capacity = 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区使用0M
   capacity = 89653248 (85.5MB)
   used     = 0 (0.0MB)
   free     = 89653248 (85.5MB)
   0.0% used

1816 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 1856
 S0C    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


E:\新建文件夹 (4)\jvm>

  • 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 15172
 NGCMN    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 15172
  NGCMN      NGCMX       NGC      S0CMX     S0C     S1CMX     S1C       ECMX        EC      YGC   FGC
  409600.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 12956
   MC       MU      CCSC     CCSU       OC          OU       YGC    FGC    FGCT     GCT
 17024.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级的缓存。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM调优工具命令详解》是一份预习资料,主要介绍了Java虚拟机(JVM)调优过程中使用的一些工具命令。这些工具命令可以帮助开发人员诊断和优化JVM的性能问题。 文中首先介绍了常用的JVM调优工具命令,包括jps、jstat、jinfo、jmap、jhat等。这些命令可以用于查看JVM进程信息、统计JVM内存和线程情况、获取JVM配置参数等。通过使用这些工具命令,开发人员可以快速定位JVM性能瓶颈所在,进行优化。 接下来,文中详细介绍了每个工具命令的使用方法和参数解释。例如,jstat命令可以用于查看JVM内存情况,包括堆内存使用量、垃圾回收情况等。而jmap命令可以用于生成堆内存转储文件,帮助开发人员分析内存泄漏问题。通过掌握这些工具命令的使用,开发人员可以更加高效地进行JVM调优。 此外,文中还介绍了一些实际的调优案例,通过使用这些工具命令来解决实际的JVM性能问题。这些案例包括内存泄漏、线程死锁、CPU占用过高等问题。通过学习这些案例,开发人员可以更好地理解如何利用工具命令来诊断和解决JVM性能问题。 总的来说,《JVM调优工具命令详解》是一份非常实用的预习资料,适合那些需要深入学习JVM性能优化的开发人员。通过学习和掌握这些工具命令,开发人员能够更加高效地进行JVM调优,提升应用程序的性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值