Java生产环境性能监控与调优—基于JDK命令行工具的监控

简介: Java生产环境性能监控与调优—基于JDK命令行工具的监控

著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

JVM参数类型

标准参数

基本上不变,相对比较稳定

  • -help
  • -server 、-client
  • -version 、-showversion
  • -cp 、-classpath

非标准化参数

在部分JVM里面会有变化,但是变化小

  • -Xint:解释执行
  • -Xcomp:第一次使用就编译成本地代码
  • -Xmixed:混合模式,JVM自己来决定是否编译成本地代码
[root@localhost tmp]# java -version java version "1.8.0_101"
Java(TM) SE Runtime Environment(build 1.8.0_101-b13)
Java HotSpot(TM)64-Bit Server vM (build 25.101-b13. nixed mode[root@localhost tmp]#Ljava -Xint -version java version "1.8.0_101"
Sava(TM)SE Runtime Environment (build 1.8.0_101-b13
Java HotSpot(TM)64-Bit Server vM (build 25.101-b13, interpreted mode  
[root@localhost tmp]# java -Xcomp-version
Ac[root@localhost tmp]# java -Xcomp -version java version "1.8.0_101
Java(TM) SE Runtime Environment(build 1.8.0_101-b13)
Java HotSpot(TM)64-Bit Server VM(build 25.101-b13,  compiled mode 
[root@localhost tmp]# 

XX参数

XX参数是非标准化参数、相对不稳定、主要用于JVM调优和Debug,分为2大类:

  • Boolean类型
    格式:-XX:[+-] 表示(+)启用或者禁用(-)name属性
    比如:-XX:+UseConMarkSweepGC
             -XX:UseG1GC
  • 非Boolean类型
    格式:-XX: =  表示name的属性值是value
    比如:-XX:MaxGCPauseMillis = 200
             -XX:GCTimeRatio = 19

-Xmx(最大JVM内存)-Xms(最小JVM内在)

它不是X参数,而是XX参数

-Xms等价于-XX:InitialHeapSize

-Xmx等价于-XX:MaxHeapSize

在linux中查看java进程内存大小 jinfo -flag MaxHeapSize [进程ID]

[ root @ localhost tmp ]# jinfo - flag MaxHeapSize 23789- XX : MaxHeapsize =268435456

如何查看JVM运行时参数

  • -XX:+PrintFlagsInitial    查看初始时的一个值
  • -XX:+PrintFlagsFinal       查看最终的一个值
  • -XX:+UnlockExperimentalVMOptions  解锁实验参数
  • -XX:+UnlockDiagnosticVMOptions    解锁诊断参数
  • -XX:+PrintCommandLineFlags    打印命令行参数

实例:java -XX:+PrintFlagsFinal -version

bool useGlGc = false 
[ product }
 bool UseGcLogFileRotation = false 
{ product }
 bool useGcoverheadLimit =true
uintx InitialHeapsize :=130023424
[ product }
 uintx MaxHeapSize :=2053111808
{ product }
 uintx MaxNewSize :@68419540

=表示默认值

:=被用户或者JVM修改后的值

jps

jps 命令类似与 linux 的 ps 命令,但是它只列出系统中所有的 Java 应用程序。 通过 jps 命令可以方便地查看 Java 进程的启动类、传入参数和 Java 虚拟机参数等信息。 具体参考jvm 性能调优工具之jps

[ weihu @iZ94r2ljas4Z order - biz - servicesl $ jps 
6304 LoggerTimer .] ar 
27085 Jps 
 Iweihu @iZ94r2ljas4Z order - biz - servicesl $ jps - l 
6304 libs / LoggerTimer . jar 
27098 sun .to0 ls . JpS . Jps 
 Iweihu @izo4r21jas4Z order - biz - services ]$

jinfo

查看一个JVM正在运行的参数值

[root@localhost tmp]# jps -1
31772 sun.tools.ips.Jps 
[root@loca7host tmp]#23789 org.apache.catalina.startup.Bootstrap jinfo -flag MaxHeapSize 23789查看tomcat最大内存 -XX:MaxHeapsize=268435456
[root@localhost tmp]# iinfo -flags 23789  查看当前进程的JVM运行时信息 
Attaching to process ID 23789, please wait... Debugger attached successfully.
JVM version is 25.101-b1 Server compiler detected不是默认JVM参数值,有被修改过,比如tomcat启动时的默认参数会修改JVM
Non-default vm flags: ECICompilerCount=3  -XX:ConcGCThreads=1 -XX:+DisableExplicitGc -XX:G1HeapReaionsize=1048576 -XX:+ 
eapoumponoutotMemorverror -XX:HeapDumpPath=nu11 AX:InitialHeapSize=268435456 -XX:Markstacksize=4194304 -XX:MaxGCPauseMi1  
is=200 -xx:MaxHeapsize=268435456 -xx:MaxMetaspaceSize=67108864’-xx:MaxNewsize=160432128 -xx:MetaspaceSize=67108864 -xx:Min ionAgeThreshold=3 HeapDe1taBvtes=1048576-XX:+Printgc -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -xX:strinaDed -XX:+UseCompressedc1assPointers -XX:+UseCompressedoops -XX:+UseFastUnorderedTimeStamps -XX:+UseG简
+UseStringDeduplication 
20 apachejuli classLoadercogiagcerfigx:ilseGucc -Djava.uti1.1ogging.config.fi1e=/usr/1oca1/tomcat8_demo/conf/logging.properties -Djava.util.logging -XX:+UseStringDeduplication -xX:StringDeduplicationAgeThreshoa
4UseCompressedc1assPointers-xx:MaxGCPauseMi11is=200-Xms256M-Xmx256M-xx:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M -XX+Disab1eExp1icitGc -XX:+HeapDumpOnoutofMemoryError -XX:HeapDumpPath=/usr/1oca1/tomcat8_demo/1ogs/-XX:+PrintGCDetails -xX+PrintGCTimeStamps -XX:+PrintGCDateStamps -x1oggc:/usr/1oca1/tomcat8_demo/1ogs/gc.1og -Djdk.t1s.ephemera1DHKeySize=2048 -D java.protoco1.handler.pkgs=org.apache.catalina.webresources -Dcatalina.base=/usr/local/tomcat8_demo -Dcatalina.home=/usr/ ocal/tomcat8_demo -Djava.io.tmpdir=/usr/local/tomcat8_demo/temp
[root@localhost tmp]# 

jinfo举例

  • 查看最大内存 jinfo -flag MaxHeapSize [进程ID]
[ root @ localhost ~]# jinfo - flag MaxHeapsize 3176 KX : MaxHeapSize =2147483648
  • 查看垃圾回收器  jinfo -flag UseConcMarkSweepGC/UseG1GC/UseParallelGC [进程ID]
[ root @ localhost ,~]# jinfo - flag useconcMarkSweepGc 3176
- XX :- UseConcMarkSweepGc 
[ root @ localhost ~]# jinfo - flag UseG1Gc3176
- XX :+ UseGlGC 
[ root @ localhost _~]# jinfo - flag useParal1elGc3176- XX :-UseParalle1Gc

使用jstat查看jvm统计信息

JDK Tools and Utilities

类加载信息

部分options:  -class, -compiler,-gc, -printcompilation更多可点此处查看

88d402baf3d9a5ffc25436f8cb10c8e.png

垃圾收集信息

部分options:  -gc, -gcutil,-gccause, -gcnew, -gcold更多可点此处查看

9b8529a01e5e614a96b496a65c1fd52.png

-gc输出结果参数说明:

  • S0C、S1C、S0U:S0和S1的总量与使用量
  • EC、EU:Eden区总量和使用量
  • OC、OU:Old区总量和使用量
  • MC、MU:Metaspace区总量和使用量
  • CCSC、CCSU:压缩类空间总量和使用量
  • YGC、YGCT:YoungGC的次数与时间
  • FGC、FGCT:FullGC的次数与时间
  • GCT:总的GC时间
  • 71bb8447b26d83571ff5ef5fa905026.png

JIT编译信息

部分options:  -compiler, -printcompilation更多可点此处查看

- ag ttag > aTTeCLTy 
[ root @ localhost ~]# jstat - compiler 3176 compiled Failed Invalid 
/ AbstractArchiveResourceSet getResource 
[ root @ localhost ~]# jstat - printcompilation 3176 compiled Size _ Type Method 
 FailedType FailedMethod 
8103 46.21
1 org / apache / catalina / webresources 
 OLU J .1 com / fasterxml / jackson / core / io / Numberoutput outputInt

jmap+MAT分析内存溢出

实例测试项目基于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 堆内存溢出图示

main ] c . i . m . MonitorTuningApplication 
180- exec -3] o . a . c . c . C .( Tomcat ]. llocalhost ]. Un 
180- exec -3] o . s . web . servlet . DispatcherServlet 
180- exec -3] o . s . web . servlet . DispatcherServlet 
 Poller -1" Exception in thread " http - nio -8080- ClientPoller - O " java . lang . OutOfMemoryError : GC overh 
: Started MonitorTuningApplication in 5.606 seconds ( JVM running for 6.212: Initializing Spring FrameworkServlet ' dispatcherServlet '
: FrameworkServlet ' dispatcherServlet ': initialization started 
: FrameworkServlet ' dispatcherServlet ': initialization completed in 37 me 

访问路径localhost:8080/noheap 非堆内存溢出图示

2018-05-23 22:40:43.077 INFO 18080---[ ost - startStop -1] o . s . b . w . servlet . FilterRegistratior 
2018-05-23 22:40:43.078 INFO 18080---[ ost - startStop -1] o . s . b . w . servlet . FilterRegistratior 
2018-05-23 22:40:43.248 INFO 18080---[
 main ] o . s . w . s . handler . SimpleUrlHandlerV 
 Exception in thread " main " java . lang . OutOfMemoryError : Metaspace 
 Exception in thread " ContainerBackgroundProcessor [ StandardEngine [ Tomcatll " java . lang 

导出应用程序内存映像文件

有2中方式可以导出,分别是:内存溢出自动导出、使用jmap命令手动导出

  • 内存溢出自动导出——使用如下jvm参数选项
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./     ##要导出的文件路径

89fb7690b8a42251c9d91c756a7647a.png

2018-05-23 22:49:43.131 INFO 20284---[ io -8080- exec -10] o . s . web . servlet . DispatcherServlet java . lang . OutOfMemoryError : GC overhead limit exceeded 
 Dumping heap to .Ajavapid20284.hprof...
 Heap dump file created [45445309 bytes in 0.241 secs ]
 Exception in thread " http - nio -8080- ClientPoller ~1" java . lang . OutOfMemoryError : GC overhead limit exceeded 
Exception in thread " http - nio -8080- exec -10" java . lang . OutOfMemoryError . GC overhead limit gxceeded 

0d51c44cb67795c96fb6cb24c6b0aaa.png

  • 使用jmap命令手动导出 部分options:  -heap, -clstats, -dump:, -F更多可点此处查看
    数据过大可能无法导出。
    下图示例:
C :\ Usersxujs > jps -1
20184
8280 sun . tools . jps . Jps 
16940 com imooc . monitor _ tuning . Moni torTuningApplication
C : Users \ xujs > cd Desktop 
C :\ Users \ xujs \ Desktop > jmap - dump : format = b ,fi1e= heap . hprof 16940
Dumping heap to C : Users ( xujs Desktop Vheap . hprof ...
Heap dump file created 

使用MAT分析定位错误

MAT安装及使用教程

内存分析工具 MAT 的使用

MAT使用进阶

jstack实战线程异常

Java的堆栈跟踪 - 为给定的进程或核心文件或远程调试服务器打印线程的堆栈跟踪。

选项 说明
-F 当jstack[ -l] pid没有响应时强制执行堆栈转储。
-l 打印有关锁的其他信息,例如拥有的可拥有java.util.concurrent同步器列表
-m 打印具有Java和本机C / C ++帧的混合模式堆栈跟踪。
-H 打印帮助信息。
-help 打印帮助信息。

java线程的状态

文献参考地址

下表列出了使用Control + Break Handler进行线程转储的可能线程状态。

线程状态 描述
NEW 该主题尚未开始。
RUNNABLE 线程正在JVM中执行。
BLOCKED 线程被阻塞等待监视器锁定。
WAITING 线程无限期地等待另一个线程执行特定操作。
TIMED_WAITING 线程正在等待另一个线程执行最多指定等待时间的操作。
TERMINATED 线程已经退出。

线程状态流转图

1bda6bb94434404b7e983a87a7e8f73.png

实例-CPU飙高

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

06583d55610d54b10399549167a341e.png

导出文件   jstack [进程ID] > [fileName]

对导出后的文件内容进行分析定位,可以参考

【JVM性能调优】jstack和线程dump分析

java运维 jstack dump日志文件详解

输出所有线程top -p [进程ID] -H

9989e9a77bff55b41543374cb978927.png

基于JVisualVM的可视化监控

监控本地tomcat

05843bfbcd1600580bbdebbd0f5fefa.png

940bf3e1ce919247fc575785fda1a64.png

84042f32995290e4b1108ff2059aa2e.png

53ab4a92586a3c911acd67bcbf3592a.png

查看死锁,循环

8ca1d68baa0d7f1b58c4f5761ad299d.png

查看热点方法执行时间

fec78d56127deadba2742a229b89089.png

实时内存

1a10ce810c41ab398afed7066dcc9f3.png

f237afdcfe0dc437188633262856792.png

修改插件中心地址

0b763312664640c12b9b233a3f92029.png

要选择jdk版本对应插件中心的地址

c3022dc2e1dc19fb40436f18d712971.png

C :\ Users \ xujs > java - version 
 java version 1.8.0_144
 Java ( TM ) SE Runtime Environment ( build 1.8.0_144-b01) 
 Java HotSpot ( TM )64- Bit Server VM ( build 25.144-b01, mixed mode )

监控远程tomcat

监控普通java进程


相关文章
|
1月前
|
Cloud Native Java 编译器
Java生态系统的进化:从JDK 1.0到今天
Java生态系统的进化:从JDK 1.0到今天
|
1月前
|
Java 编译器 测试技术
滚雪球学Java(03):你知道JDK、JRE和JVM的不同吗?看这里就够了!
【2月更文挑战第12天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!!
103 4
|
2月前
|
监控 安全 Java
探索Java的未来:JDK 18新特性全览
探索Java的未来:JDK 18新特性全览
67 0
|
2月前
|
缓存 安全 Java
JDK 14全景透视:每个Java开发者必知的新特性
JDK 14全景透视:每个Java开发者必知的新特性
47 0
|
2月前
|
JavaScript 前端开发 安全
Java新纪元:一探JDK 15的全新特性
Java新纪元:一探JDK 15的全新特性
43 0
|
2月前
|
JSON 编解码 Java
Java升级:JDK 9新特性全面解析“
Java升级:JDK 9新特性全面解析“
29 0
|
2月前
|
安全 Java API
JDK 11中的动态类文件常量:探索Java字节码的灵活性与动态性
在JDK 11中,Java语言引入了一个新的特性,允许在运行时动态地修改类文件常量。这一特性为Java开发者提供了更大的灵活性,使他们能够根据需要在运行时更改类文件中的常量值。本文将深入探讨动态类文件常量的工作原理、优点、限制以及在实际项目中的应用。
45 11
|
2月前
|
SQL 缓存 监控
drds性能监控与调优
drds性能监控与调优
32 2
|
3月前
|
SQL 存储 监控
dts性能监控与调优
dts性能监控与调优
28 1
|
1月前
|
Java
916.【Java】javap 查看 class 文件的jdk编译版本
916.【Java】javap 查看 class 文件的jdk编译版本
45 2