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月前
|
安全 Java 编译器
JDK 10中的局部变量类型推断:Java编程的简化与革新
JDK 10引入的局部变量类型推断通过`var`关键字简化了代码编写,提高了可读性。编译器根据初始化表达式自动推断变量类型,减少了冗长的类型声明。虽然带来了诸多优点,但也有一些限制,如只能用于局部变量声明,并需立即初始化。这一特性使Java更接近动态类型语言,增强了灵活性和易用性。
106 53
|
1月前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
112 38
|
23天前
|
Oracle 安全 Java
深入理解Java生态:JDK与JVM的区分与协作
Java作为一种广泛使用的编程语言,其生态中有两个核心组件:JDK(Java Development Kit)和JVM(Java Virtual Machine)。本文将深入探讨这两个组件的区别、联系以及它们在Java开发和运行中的作用。
48 1
|
1月前
|
IDE Java 编译器
开发 Java 程序一定要安装 JDK 吗
开发Java程序通常需要安装JDK(Java Development Kit),因为它包含了编译、运行和调试Java程序所需的各种工具和环境。不过,某些集成开发环境(IDE)可能内置了JDK,或可使用在线Java编辑器,无需单独安装。
65 1
|
1月前
|
SQL 监控 Java
Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面
本文探讨了Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,以实现高效稳定的数据库访问。示例代码展示了如何使用HikariCP连接池。
19 2
|
1月前
|
Prometheus 监控 Cloud Native
JAVA线程池监控以及动态调整线程池
【10月更文挑战第22天】在 Java 中,线程池的监控和动态调整是非常重要的,它可以帮助我们更好地管理系统资源,提高应用的性能和稳定性。
95 4
|
1月前
|
Prometheus 监控 Cloud Native
在 Java 中,如何使用线程池监控以及动态调整线程池?
【10月更文挑战第22天】线程池的监控和动态调整是一项重要的任务,需要我们结合具体的应用场景和需求,选择合适的方法和策略,以确保线程池始终处于最优状态,提高系统的性能和稳定性。
254 2
|
2月前
|
缓存 Java Maven
java: 警告: 源发行版 11 需要目标发行版 11 无效的目标发行版: 11 jdk版本不符,项目jdk版本为其他版本
如何解决Java项目中因JDK版本不匹配导致的编译错误,包括修改`pom.xml`文件、调整项目结构、设置Maven和JDK版本,以及清理缓存和重启IDEA。
61 1
java: 警告: 源发行版 11 需要目标发行版 11 无效的目标发行版: 11 jdk版本不符,项目jdk版本为其他版本
|
2月前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
33 0
[Java]静态代理与动态代理(基于JDK1.8)
|
2月前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
30 1
下一篇
DataWorks