JVM--内置命令行工具 & 内置图形化工具

一、内置命令行工具

  JDK内置的命令行工具默认在JDK的安装目录下的bin目录或者在jre所在目录的bin目录下,内置的命令行工具也可以再细分为开发、编译、分发、安全工具和运行期工具。

    开发、编译、分发、安全工具:

      java、javac、javap:运行、编译、反编译,最常用的三个命令

      javadoc:根据Java代码和标准注释,自动生成相关的API说明文档

      javah:在做JNI开发时,Java代码需要调用其他语言编写的代码,例如C或C++,或者其他代码调用Java代码,都需要将Java代码变成对应代码,例如变成C语言调用C语言,定义这些方法的头文件就是.h文件

      extcheck:用来检查jar包运行时有没有版本冲突

      jdb:java的本地调试器,不过现在一般都用Idea进行调试,这种方式很少使用

      jdeps:探测class或jar包需要的依赖,主要是分析import,其实就是jdependency的缩写

      jar:打包工具

      keytool:安全证书和秘钥管理工具,负责生成、导入、导出秘钥操作

      jarsigner:jar文件签名和验证工具,当我们向外分发jar包时,就需要带上一个签名,同时提供一种验证方式,表明jar包分发出去后没有被篡改过

      policytool:跟JVM安全相关,其本质上是一个图形界面管理工具,主要是用来管理本机的Java安全策略,例如要求Java当前的安全沙箱有些类是不能调用的,有些资源是不能访问的。

        

    运行期工具:

      主要包括jps/jinfo、jstat、jmap、jstack、jhat、jcmd、jrunscript/jjs

        

(一)jps/jinfo:虚拟机进程状况工具

  1、jpd

  该命令主要用于显示目前运行的java进程,命令格式:jsp [options] [hostid]

  可以跟随的命令:-q:只显示进程号,不显示类名等信息;-l:显示进程号和类名,如果执行的是jar包,则显示jar包的路径;-m:输出虚拟机进程启动时传递给主函数的参数;-v:显示虚拟机启动时的参数

   例如可以使用 jps -lmv输出虚拟机启动时的所有参数、进程号、jar包等内容:

        

  在使用jps命令时,有两点需要注意,一个是用户权限,一个是jdk版本,例如在操作系统上如果有多个用户,当前用户权限不够大,不能看其他用户下运行的java进程,那么使用jps命令看到的java进程是不全的,这时就需要使用sudo命令切换到管理员权限查看,如果在一个操作系统上使用多个不同版本的虚拟机运行了多个JVM进程,那么使用其中一个虚拟机的jps命令查看到的结果也有可能是不全的,所以在一个服务器上只安装一个版本的JDK。

  2、jinfo

  jinfo命令用来实时查看和调整虚拟机各项参数。

  命令格式:jinfo [option] <pid>

  option可以是 -flag,表示要查看的内容,flag可以用java -XX:+PrintFlagsFinal来查看全量的标识。sysprops可以把虚拟机的所有配置文件打印出来。

  命令样例:

      

(二)jstat:虚拟机统计信息监视工具

  该命令用于监控虚拟机各种运行状态信息,它可以显示本地或远程虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据,在没有GUI可视化界面、只有文本控制台的服务器上,它是运行期定位虚拟机问题的常用工具。

  命令格式:jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

  如果是远程服务器,则vmid为[protocol:][//]lvmid[@hostname[:port]/servername]

  参数interval和count表示查询间隔和次数,如果省略了这两个参数,则说明只查询一次

  option表示要查询的虚拟机信息,例如类加载、垃圾收集、运行期即时编译状况,具体参数如下所示:

    -class:类加载信息统计,例如显示类加载数量、加载总空间、卸载数量、卸载总空间及类装载所消耗的时间

    -compiler 输出运行时即时编译器编译过的方法和耗时信息

    -gc 监视java堆状况,包括Eden区、两个Survivor区、老年代、永久代等的容量、已用空间、垃圾收集时间合计等信息,用法: jstat -gc -h 10 -t 864 1s 20

    -geccapatity 各个内存池分代空间的容量(监视内容基本与-gc一致,但输出主要关注java堆各个区域使用到的最大、最小空间)

    -gcutil GC相关区域的使用率统计(监控内容基本与-gc一致,但是输出主要关注已使用空间占总空间的百分比)

    -gccause 看上次GC、本次GC的原因(监控内容与-gcutil一致,但是输出为额外加上导致上一次gc的原因)

    -gcnew 监视新生代垃圾回收情况

    -gcnewcapaticy 监视内容与-gcnew一致,但是会额外输出新生代使用的最大、最小空间

    -gcold 监视老年代的回收情况

    -gcoldcapaticy old空间大小统计(监视内容与-gcold一致,但是会额外输出老年代使用的最大、最小空间)

    -gcpermcapaticy 元空间大小统计(监控永久代使用的最大、最小空间)

    -printcompilation 输出已经被即时编译器编译的方法

   在这些参数中,最常用的是-gc和-gcutil:从下图可以看到,gc信息每1000ms输出一次,共输出5次,-gc展示了每个分代的字节数、个代GC次数和时间,而使用-gcutil展示的是分代字节数的比例,而不是数值,别的展示内容都一致。

        

   对于每一项的具体含义如下图所示:打印时间戳要使用-t : jstat -gc pid 1000 5

                

(三)jmap:java内存映像工具

  jmap命令用于Dump堆内存快照,和jstat区别是jstat可以动态的打印,而jmap是打印的快照。

  命令格式:jmap [option] <pid>,常用的有 -heap、-histo、-dump,这三个命令都是dump堆的快照,不同的是dump的粒度和侧重点不一样,-dump是dump快照到一个文件中,是一个详细的信息,可以使用其他工具对文件进行分析,-heap是按照分代汇总信息,打印堆内存的配置和使用,而-histo是按照类的汇总信息,打印哪个类占用的空间最多。

    -heap 打印堆内存/内存池的配置和使用信息(显示java堆详细信息,如使用哪种回收器、参数配置、分代状况等,只在linux和solaris平台有效)

    -histo 看哪些类占用的空间最多,直方图(显示堆中统计信息,包括类、实例数量、合计容量)

    -dump 堆Dump堆内存(转快照,格式为-dump:[live]format=b,file=<filename>,其中live子参数说明是否只dump出存活的对象),果不使用该命令,也可以使用一些暴力手段,例如使用-XX:+HeapDumpOutOfMemoryError参数,通过该参数,可以让虚拟机在内存溢出时自动生成堆转储快照文件,也可以通过-XX:HeapDumpOnCtrlBreak参数并使用Ctrl+Break键生成堆转快照文件,也可以在linux系统下使用kill -3命令让虚拟机生成堆转快照文件

    -finalizerinfo 显示在F-Queue中等待finalizer线程执行finalize方法的对象,该命令只在linux和Solaris平台有效

    -permstat 以ClassLoader为统计口径显示永久代内存状态,只在linux和solaris平台有效

    -F 当虚拟机对-dump命令没有响应时,可通过该命令强制生成dump快照,只在linux和solaris平台有效

  命令样例:

                

  这里说明一下,jinfo和jmap在mac的高版本系统中使用是有问题的,在JDK10之后才修复,但是在windows和linux上使用是没有问题的。

(四)jstack:java堆栈跟踪工具

  jstack命令用于生成虚拟机当前时刻的线程快照,线程快照是当前虚拟机每一条线程正在执行的方法堆栈的集合。

  生成堆栈快照的目的一般是用来定位线程长时间停顿的原因,例如死锁、死循环、请求外部资源导致的长时间挂起等,都是导致线程长时间停顿的原因。

  命令格式:jstack [ option ] vmid

    -F 强制执行 thread dump,可在 Java 进程卡死(hung 住)时使用,此选项可能需要系统权限,此选项可能需要系统权限。

    -m 混合模式(mixed mode),将 Java 帧和native 帧一起输出,此选项可能需要系统权限。

    -l 长列表模式,将线程相关的 locks 信息一起输出,比如持有的锁,等待的锁。

  命令示例:

      

  在linux和mac操作系统下,可以使用kill -3 pid 使其打印出来堆栈信息(kill -9是强制kill,kill -3后会在jvm运行的窗口打印堆栈信息)

 

(五)jcmd

  Jcmd 综合了前面的几个命令,在高版本的JDK中提供,一般在高版本的JDK中,也推荐使用jcmd命令。示例:

    jcmd pid VM.version:查看VM版本

    jcmd pid VM.flags:查看VM参数

    jcmd pid VM.command_line:查看命令行信息

    jcmd pid VM.system_properties:查看系统环境变量信息

    jcmd pid Thread.print:打印线程堆栈

    jcmd pid GC.class_histogram:堆内存直方图

    jcmd pid GC.heap_info:dump堆内存

  如果记不住,可以使用 jcmd pid help 查看全量可以使用的命令

        

(六)jrunscript/jjs:运行Javascript脚本或脚本片段命令

  jrunscript和jjs底层都是javascript的Java引擎Rhino,jrunscript命令是用来直接运行一段javascript代码或者一段javascript文件,jjs是一个交互式的Javascript执行环境,就类似一个shell。

  jrunscript有一些比较神奇的用法,例如我们的电脑中没有装 curl 命令时,可以使用jrunscript -e来模拟一个curl命令,例如访问百度:jrunscript -e "cat('http://www.baidu.com')",同时也可以将多条javascript命令写成一个js脚本,使用 -f 参数来指定其运行。

        

    执行 js 脚本片段:jrunscript -e "print('hello,kk.jvm'+1)"   

    执行 js 文件:jrunscript -l js -f /XXX/XXX/test.js

  jjs:是一个交互式的控制台,类似于shell

        

二、可视化故障处理工具

  JDK中除了提供了大量的命令行工具外,还提供了JConsole、JHSDB、VisualVM、和JMC四个可视化工具。

(一)JConsole:JAVA监视与管理控制台

  Jconsole用来监控当前JVM的CPU使用率,堆内存、堆外内存以及各个分代的内存使用情况。

  1、概述

  JConsole主要功能是对系统进行信息收集和参数动态调整。可以通过JDK/bin目录中的jconsole.exe启动,启动后就可以看到本地运行的所有java进程,同时也可以访问远程的Java进程。

  启动后jconsole.exe或输入jconsole命令后,可以看到相关进程,点击进入,可以看到系统运行的情况,如果是远程的服务,在打开JMX协议的时候就可以输入JMX的URL,就可以远程访问别的机器上对应的JVM进程。

          

  从上图可以看到,监控台有概述、内存、线程、类、VM摘要、MBean供6个选项,其中概述里面显示的是整个虚拟机主要运行数据的概要信息,包括堆的使用情况、线程、类、CPU使用情况的曲线图,可以对JVM的运行情况有一个初步的了解在·。

  2、内存监控(类似jstat命令)

  在内存页可以看各个分代及整个堆空间的使用情况,可以选择查看具体分代的内存使用,可以选择时间范围,还可以手动执行GC,同时在图形界面左下角是内存总体的使用情况,右下角使用柱状图的形式看堆和非堆是使用率,点击可以查看具体分代的使用情况。

                    

   3、线程监控(类似jstack命令)

  查看各个线程,就可以看到线程的运行情况。可以选择时间范围,在上方的图形界面可以看到当前活动的线程和峰值,在左下角有所有存活的线程,点击具体一个线程,可以查看该线程的具体情况;另外在下方还有检测死锁的按钮,点击后可以进行检测。

        

  4、类监控

  可以查看加载类的总数、当前加载的综述、已卸载的数量等信息。

        

  5、VM概要

  VM概要信息中心展示了进程号、运行的jar包、运行时间、JDK版本、编译时间、线程信息、类加载信息、堆栈信息、操作系统、VM参数等。

        

(二)VisualVM:多合-故障处理工具

  1、概要

  VisualVM是功能最强大的运行监控和故障处理程序之一,他除了常规的运行监视、故障处理外,还将提供其他方面的能力,譬如性能分析等。

  VisualVM基于NetBeans平台开发工具,所以其具备通过插件扩展功能的能力,有了插件的支持,VisualVM可以做到:

    显示虚拟机进程以及进程的配置、配置信息(jps、jinfo)

    监视应用程序的处理器、垃圾收集、堆、方法区、以及线程的信息(jstack、jstat)

    dump堆转快照分析(jmap、jhat)

    方法级的性能分析,找出被调用最多、执行时间最长的方法

    离线程序快照:收集程序运行时配置、线程dump、内存dump、等信息建立一个快照,可以将快照发送给开发者进行分析

    其他插件带来的其他功能

  打开JDK下bin目录中的jvisualvm.exe,即可打开,但是对于JDK8稍微大点的版本及后续版本,JDK做了瘦身,不再将VisualVM放入JDK中,我们直接在官网下载即可(下载地址:https://visualvm.github.io/download.html)。

  VisualVM可以安装许多插件,在打开的软件中选择“工具-插件”,然后在可用插件中即可查看可用的插件。

  从下图可以看到,在VisualVM中,有概述、监视、线程、抽样器、Profiler等内容,其中概述、监视、线程等与JConsole差别不大。线程的可视化比jconsole做的更好,可以通过颜色来判断线程的状态。

                

   2、生成、浏览堆转储快照

  在左侧的java进程上,可以选择生成线程Dump和堆Dump,然后就可以对线程或堆进行分析。

    摘要:可以看到程序dump时的运行参数、环境参数线程堆栈等信息;

    类:以类为统计口径进行统计类的实例数量、容量信息;

    实例数:不可以直接进入,需要从类中选择具体的类进入

    OQL:运行OQL查询语句,与jhat中的OQL功能一样。

    

 

   3、Profiler:分析程序性能

  VisualVM对比Jconsoal真正强大的地方在于他的抽样器和profiler。

    通过抽样器可以监视运行一段时间内系统内发生了什么,例如运行了那些线程、线程中运行了哪些方法,方法占用了多长时间,有没有什么热点方法和热点代码等,也可以对内存进行抽样,例如上面使用jmap -histo可以查看具体一个时间点时堆内存中所有对象的类型、数量和占用空间数,但是如果想要查看一段时间内的上述信息,就可以使用VosialVM的抽样器进行查看

        

    在Profiler页签中,VisualVM提供了程序运行期间方法级的处理执行时间分析以及内存分析,但是做Profiling分析肯定会对程序运行性能有比较大的影响,所以一般不会在生产环境中使用,如果想要使用,可以改为下面的JMC来完成,JMC的Profiling能力更强,对应用的影响非常轻微。

    

  4、BTrace动态日志跟踪

  其作用是在不中断目标程序运行的前提下,通过Hotspot虚拟机的Instrument功能动态加入原本并不存在的调试代码。

        

 

   我这里在线安装时,显示网络问题,所以选个手动离线安装,访问:https://visualvm.github.io/uc/8u131/updates.html,选择BTrace Workbench,下载完毕后,在“工具-插件”已下载中选择下载的插件

   

 

   下面是样例代码:

public class DemoTest3 {
    public static void main(String[] args) throws Exception {
        for (int i=0; i< 10000; i++){
            Thread.sleep(500);
            int a = (int) Math.round(Math.random() * 1000);
            int b = (int) Math.round(Math.random() * 1000);
            System.out.println("=================" + add(a,b));
        }
    }
    public static int add(int a, int b){
        return a+b;
    }
}

  然后在VisualVM中选中DemoTest3进程,右键选择Trance Application,就会出现Trance的界面,在Trance界面中编写测试代码

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class TracingScript {
    @OnMethod(clazz="com.lcl.jdk8emo.DemoTest3",method="add",location=@Location(Kind.RETURN))
    public static void func(@Self com.lcl.jdk8emo.DemoTest3 instance,int a,int b,@Return int result){
        println("调用堆栈:");
        jstack();
        println(strcat("方法参数a:", str(a)));
        println(strcat("方法参数b:", str(b)));
        println(strcat("结果:", str(result)));
    }
}

  在测试程序启动后,点击start,即可实时查看程序运行结果:

    BTrace的用途很广泛,打印调用堆栈、参数、返回值只是它最基础的使用形式,其还可以进行性能监控、定位连接泄露、内存泄漏、解决多线程竞争问题等的使用案例。

  阿里开源的诊断工具Arthas也是通过Instrument实现了与BTrace类似的功能

(三)Visual GC

  如果不想安装或者没有安装Visual VM,可以在Eclipse或Idea中安装插件Visual GC,安装后在右下角点击Visual GC即可。

        

 (四)JMC(Java Mission Control):可持续在线的监控工具

  JMC是目前JVM分析工具中功能最强大的一款工具。

  点击JDK/bin目录下的JMC.exe文件,会显示本地运行的java进程,也可以在“JVM浏览器”页右键创建远程连接,连接后,操作内容与本地一样。和Visual VM一样,高版本的JDK不再提供JMC,可以自行到官网下载。

  点开JVM浏览器可以看到,每个进程的数据都有MBean和JFR两个数据来源。

  1、MBean

  MBean这部分数据与JConsole和VisualVM上取到的内容是一样的,只是展示内容有所不同。

    

  2、JFR

  双击飞行记录器,如果提示没有开启,则需要对其进行开启(开启飞行器:-XX:+UnlockCommercialFeatures -XX:+FlightRecorder)。

  双击后点击完成,则会进行飞行记录,可以进行记录时间、垃圾收集器、编译器、方法采样、线程记录、异常记录、网络和文件IO、事件记录等选项和频率设定。其基本的工作逻辑是开启一系列事件的录制动作,当某个事件发生时,这个事件的所有上下文数据将会以循环日志的形式被保存至内存或者指定的某个文件当中,循环日志相当于数据流被保留在一个环形缓存中,所以只有最近发生的事件的数据才是可用的。JMC从虚拟机内存或者文件中读取并展示这些事件数据,并通过这些数据进行性能分析。

  飞行记录报告里包含以下几类信息:

    一般信息:关于虚拟机、操作系统和记录的一般信息

    内存:关于内存管理和垃圾收集的信息

    代码:关于方法、异常错误、编译和类加载信息

    线程:关于应用程序中线程和锁的信息

    IO:关于文件和套接字输入输出的信息

    系统:关于正在运行java虚拟机的系统、进程和环境变量的信息

    事件:关于记录中的事件类型信息,可以根据线程或堆栈跟踪,按照日志或图形的格式查看。

    

  飞行记录会把这个时间段内JVM内部的所有变化都记录下来,同时对这些内容作了加工和分析,例如垃圾回收,会分析最短用时,最长用时,平均用时等;线程栈上内存分配量,分配速率,时间段内发生了多少次GC,每次GC本身的数据记录及分析等。

  除了内存和CPU,飞行记录器还会基于收集的这些信息,对代码进行分析,例如热点包、热点类、热点方法、热点线程、热点执行了多少次,所占用的百分比等,这样我们就可以知道,我们系统的哪些地方是系统瓶颈,也是在做优化分析时着重需要注意的点。

                                 

三、第三方工具

(一)GCEasy

  https://gceasy.io/

  业界首先采用机器学习算法解决GC日志分析问题,GCeasy内置机器智能可以自动检测JVM和Android GC日志中的问题,并推荐解决方案。

  GC日志分析是免费的,Machine Learning收费

    几秒内解决GC和内存问题

    JVM堆优化建议

    机器学习算法

(二)MAT

  MAT是一个强大的内存分析工具,可以快捷、有效地帮助我们找到内存泄露,减少内存消耗分析工具。MAT是Memory Analyzer tool 的缩写,是一种快速,功能丰富的Java堆分析工具,能帮助你查找内存泄漏和减少内存消耗。很多情况下,我们需要处理测试提供的hprof文件,分析内存相关问题,那么MAT也绝对是不二之选。

  使用MAT,可以轻松实现以下功能:

    找到最大的对象,因为MAT提供显示合理的累积大小(retained size)

    探索对象图,包括inbound和outbound引用,即引用此对象的和此对象引出的

    查找无法回收的对象,可以计算从垃圾收集器根到相关对象的路径

    找到内存浪费,比如冗余的String对象,空集合对象。

  MAT安装有两种方式,一种是以eclipse插件方式安装,一种是独立安装。在MAT的官方文档中有相应的安装文件下载,下载地址为:https://www.eclipse.org/mat/downloads.php

    若使用eclipse插件安装,help -> install new soft点击ADD,在弹出框中添加插件地址:http://download.eclipse.org/mat/1.9.0/update-site/,也可以直接在下载页面下载离线插件包,以离线方式安装。

    也可以选择独立安装

  MAT相关概念说明

    内存泄漏与内存溢出

      内存泄露:对象已经没用了(不被任何程序逻辑所需要),还存在被根元素引用的情况,无法通过垃圾收集器进行自动回收,需要通过找出泄漏的代码位置和原因,才好确定解决方案;

      内存溢出:内存中的对象都还存活着,JVM的堆分配空间不足,需要检查堆设置大小(-Xmx与-Xms),代码是否存在对象生命周期太长、持有状态时间过长的情况。

    引用(强引用,软引用,弱引用,虚引用)

      Strong Ref(强引用):强可达性的引用,对象保存在内存中,只有去掉强可达,对象才被回收,通常我们编写的代码都是Strong Ref。

      Soft Ref(软引用):对应软可达性,只要有足够的内存,就一直保持对象,直到发现内存吃紧且没有Strong Ref时才回收对象。一般可用来实现缓存,通过java.lang.ref.SoftReference类实现。

      Weak Ref(弱引用):比Soft Ref更弱,当发现不存在Strong Ref时,立刻回收对象而不必等到内存吃紧的时候。通过java.lang.ref.WeakReference和java.util.WeakHashMap类实现。

      Phantom Ref(虚引用):根本不会在内存中保持任何对象,你只能使用Phantom Ref本身。一般用于在进入finalize()方法后进行特殊的清理过程,通过 java.lang.ref.PhantomReference实现。

    shallow heap及retained heap

      shallow heap:对象本身占用内存的大小,也就是对象头加成员变量(不是成员变量的值)的总和,如一个引用占用32或64bit,一个integer占4bytes,Long占8bytes等。如简单的一个类里面只有一个成员变量int i,那么这个类的shallo size是12字节,因为对象头是8字节,成员变量int是4字节。常规对象(非数组)的Shallow size有其成员变量的数量和类型决定,数组的shallow size有数组元素的类型(对象类型、基本类型)和数组长度决定。

      retained heap:如果一个对象被释放掉,那会因为该对象的释放而减少引用进而被释放的所有的对象(包括被递归释放的)所占用的heap大小,即对象X被垃圾回收器回收后能被GC从内存中移除的所有对象之和。相对于shallow heap,Retained heap可以更精确的反映一个对象实际占用的大小(若该对象释放,retained heap都可以被释放)。

    outgoing references与incoming references

      outgoing references :表示该对象的出节点(被该对象引用的对象)。

      incoming references :表示该对象的入节点(引用到该对象的对象)。

    Dominator Tree

      将对象树转换成Dominator Tree能帮助我们快速的发现占用内存最大的块,也能帮我们分析对象之间的依赖关系。Dominator Tree有以下几个定义:

        对象X Dominator(支配)对象Y,当且仅当在对象树中所有到达Y的路径都必须经过X对象Y的直接Dominator,是指在对象树中距离Y最近的Dominator

        Dominator tree利用对象树构建出来。在Dominator tree中每一个对象都是他的直接Dominator的子节点。

    对象树和Dominator tree的对应关系如下:

         

     如上图,因为A和B都引用到C,所以A释放时,C内存不会被释放。所以这块内存不会被计算到A或者B的Retained Heap中,因此,对象树在转换成Dominator tree时,会A、B、C三个是平级的。 

  MAT工具使用:

    分析内存溢出

    分析内存泄露

    查看对象个数及对象内存占用

    观察对象回收后释放空间大小

    观察线程栈

(三)GCViewer

  GCViewer是一款开源的GC日志分析工具。项目的 GitHub 主页对各个指标提供了完整的描述信息 需要安装jdk才能使用。借助GCViewer日志分析工具,可以非常直观地分析出待调优点。可从以下几方面来分析:

    Memory:分析Totalheap、Tenuredheap、Youngheap内存占用率及其他指标,理论上内存占用率越小越好;

    Pause:分析Gc pause、Fullgc pause、Total pause三个大项中各指标,理论上GC次数越少越好,GC时长越小越好;

  配置开启GC日志

    some support for OpenJDK 9 / 10 unified logging format -Xlog:gc:, the following configurations will work

      -Xlog:gc:file="path-to-file" (uses defaults)

      -Xlog:gc=info:file="path-to-file":tags,uptime,level (minimum configuration needed)

      -Xlog:gc*=trace:file="path-to-file":tags,time,uptime,level (maximum configuration supported, additional tags ok, but ignored; additional decorations will break parsing)

    Oracle JDK 1.8 -Xloggc: [-XX:+PrintGCDetails] [-XX:+PrintGCDateStamps]

    Sun / Oracle JDK 1.7 with option -Xloggc: [-XX:+PrintGCDetails] [-XX:+PrintGCDateStamps]

  用法:下载https://github.com/chewiebug/GCViewer/releases

# 分析gc日志,打开图形化界面 
java -jar gcviewer-1.36.jar gc.log 
# 分析gc日志,不打卡图形化界面,将结果直接生成 
java -jar gcviewer-1.36.jar gc.log summary.csv chart.png

  图表 + 摘要:

        

  统计图表:

            

  顶部的黑色线都代表Full GC,也可以理解为Major GC,是根据日志中的CMS GC统计的;底部灰色线代表的是Minor GC。

             

   摘要summary

        

(四)Arthas

  下一步就该使用 JVM 命令进行问题定位了,但很多研发可能由于自身工作经验不足、对 Java 内存模型理解不深、尚未掌握 JVM 排查命令等原因对 JVM 相关排查畏手畏脚,不够自信,进而影响排查进度。针对这种情况,本文推荐一款开源的 Java 诊断工具,对 JVM不熟的研发可以尝试学习使用下。相对 JVM 命令来说简单多了。

  Arthas 是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。

  当你遇到以下类似问题而束手无策时,Arthas 可以帮助你解决:

    这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?

    我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?

    遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?

    线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!

    是否有一个全局视角来查看系统的运行状况?

    有什么办法可以监控到 JVM 的实时运行状态?

    怎么快速定位应用的热点,生成火焰图?

  Arthas 支持 JDK 6+,支持 Linux、Mac、Winodws,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

  1、下载安装与启动 

# 下载arthas-boot.jar 
curl -O https://alibaba.github.io/arthas/arthas-boot.jar 
# 打印帮助信息: 
java -jar arthas-boot.jar -h 
# 启动 
java -jar arthas-boot.jar 
# 选择应用java进程: 
[INFO] arthas-boot version: 3.5.3 
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER. 
* [1]: 31480 org.apache.catalina.startup.Bootstrap 
[2]: 30108 /root/hero_web-1.0-SNAPSHOT-default.jar 
# Demo进程是第2个,则输入2,再输入回车/enter。Arthas会attach到目标进程上,并输出日志:
 [INFO] Start download arthas from remote server: https://arthas.aliyun.com/download/3.5.4?

   2、Arthas 常见命令

      jvm:查看当前 JVM 的信息

      thread:查看当前 JVM 的线程堆栈信息

        -b 选项可以一键检测死锁

        -n 指定最忙的前N个线程并打印堆栈

      trace:方法内部调用路径,并输出方法路径上的每个节点上耗时,服务间调用时间过长时使用

      stack:输出当前方法被调用的调用路径

      Jad:反编译指定已加载类的源码,反编译便于理解业务

      logger:查看和修改 logger,可以动态更新日志级别

    支持管道:

      Arthas支持使用管道对上述命令的结果进行进一步的处理,如 sm java.lang.String * | grep 'index'

        grep——搜索满足条件的结果

        plaintext——将命令的结果去除ANSI颜色

        wc——按行统计输出结果

    后台异步任务:

      当线上出现偶发的问题,比如需要watch某个条件,而这个条件一天可能才会出现一次时,异步后台任务就派上用场了

        使用 > 将结果重写向到日志文件,使用 & 指定命令是后台运行,session断开不影响任务执行(生命周期默认为1天)

        jobs——列出所有job

        kill——强制终止任务

        fg——将暂停的任务拉到前台执行

        bg——将暂停的任务放到后台执行

    用户数据回报:

      在 3.1.4 版本后,增加了用户数据回报功能,方便统一做安全或者历史数据统计。

      在启动时,指定 stat-url ,就会回报执行的每一行命令,比如: ./as.sh --stat-url 'http://192.168.10.11:8080/api/stat'

      在tunnel server里有一个示例的回报代码,用户可以自己在服务器上实现

      这里只列出常用命令,完整列表参考命令列表:

      https://alibaba.github.io/arthas/commands.html

  其他特性

    异步命令支持

    执行结果存日志

    批处理的支持

    ognl表达式的用法说明

  Arthas 使用上相对 JVM 命令简单很多,即便是工作年限不多的小伙伴学起来也很快。熟练使用 Arthas 应该能诊断出大部分线上应用问题,但生产环境通常不允许擅自拷贝 jar 包,而且 Arthas 会拖慢应用本身,如果条件不允许,又该如何诊断呢?这边简单介绍下 jdk 自带的命令行工具。 

   3、查看dashboard

    输入dashboard,按 回车/enter ,会展示当前进程的信息,按 ctrl+c 可以中断执行。

        

   4、查看线程thread

    通过thread命令来获取到应用进程的线程信息,thread -1 会打印线程统计信息。 

            

   5、反编译已加载类源码

    运行期通过jad来反编译项目代码

         

   6、监听运行时方法的返回值watch

    通过watch命令来查看 com.hero.web.user.controller#UserController 函数的返回值: 

         

  7、退出

    如果只是退出当前的连接,可以用 quit 或者 exit 命令。Attach到目标进程上的arthas还会继续运行,端口会保持开放,下次连接时可以直接连接上。如果想完全退出arthas,可以执行 stop 命令。 

四、JVM参数:标准参数、非标准参数、不稳定参数

  在JVM调整过程中,主要是对JVM参数做的调整,以下我们队JVM主要参数做逐一介绍。JVM参数有很多,其实我们直接使用默认的JVM参数,不去修改都可以满足大多数情况。但是如果你想在有限的硬件资源下,部署的系统达到最大的运行效率,那么进行相关的JVM参数设置是必不可少的。下面我们就来对这些JVM参数进行详细的介绍。

  JVM参数主要分为以下三种:标准参数、非标准参数、不稳定参数。

  1、标准参数

    顾名思义,标准参数中包括功能以及输出的结果都是很稳定的,基本上不会随着JVM版本的变化而变化。标准参数以-开头,如:java -version、java -jar等,通过java -help可以查询所有的标准参数,我们可以通过 -help 命令来检索出所有标准参数。

         

     -help 也是一个标准参数,再比如使用比较多的 -version也是。

   2、非标准参数

    非标准参数以-X开头,是标准参数的扩展。对应前面讲的标准化参数,这是非标准化参数。表示在将来的JVM版本中可能会发生改变,但是这类以-X开始的参数变化的比较小。

    我们可以通过 Java -X 命令来检索所有-X 参数。

        

     我们可以通过设置非标准参数来配置堆的内存分配,常用的非标准参数有:

      1. -Xms堆内存的最小值:默认值是总内存/64(且小于1G),默认情况下,当堆中可用内存小于40%(这个值可以用-XX:MinHeapFreeRatio 调整,如-X:MinHeapFreeRatio=30)时,堆内存会开始增加,一直增加到-Xmx的大小。

      2. -Xmx堆内存的最大值:默认值是总内存/64(且小于1G),如果Xms和Xmx都不设置,则两者大小会相同,默认情况下,当堆中可用内存大于70%时,堆内存会开始减少,一直减小到-Xms的大小;

      3. -Xmn新生代内存的最大值:包括Eden区和两个Survivor区的总和,写法如:-Xmn1024,-Xmn1024k,-Xmn1024m,-Xmn1g

      4. -Xss每个线程的栈内存:默认1M,一般来说是不需要改的。

      5. -Xloggc:file与-verbose:gc功能类似,只是将每次GC事件的相关情况记录到一个文件中,文件的位置最好在本地,以避免网络的潜在问题。

  3、不稳定参数

    这是我们日常开发中接触到最多的参数类型。这也是非标准化参数,相对来说不稳定,随着JVM版本的变化可能会发生变化,主要用于JVM调优和debug。

    不稳定参数以-XX 开头,此类参数的设置很容易引起JVM 性能上的差异,使JVM存在极大的不稳定性。如果此类参数设置合理将大大提高JVM的性能及稳定性。

    不稳定参数分为三类:

      性能参数:用于JVM的性能调优和内存分配控制,如内存大小的设置;

      行为参数:用于改变JVM的基础行为,如GC的方式和算法的选择;

      调试参数:用于监控、打印、输出jvm的信息;

    不稳定参数语法规则:

      1. 布尔类型参数值:

        -XX:+,'+'表示启用该选项

        -XX:-,'-'表示关闭该选项

        示例:-XX:+UseG1GC:表示启用G1垃圾收集器

      2. 数字类型参数值:

        -XX:=给选项设置一个数字类型值,可跟随单位,例如:'m'或'M'表示兆字节;'k'或'K'千字节;'g'或'G'千兆字节。32K与32768是相同大小的。

        示例:-XX:MaxGCPauseMillis=500 :表示设置GC的最大停顿时间是500ms

      3. 字符串类型参数值:

        -XX:=给选项设置一个字符串类型值,通常用于指定一个文件、路径或一系列命令列表。

        示例:-XX:HeapDumpPath=./dump.core

 

posted @ 2021-07-14 00:57  李聪龙  阅读(281)  评论(0编辑  收藏  举报