JVM 调优工具

JDK 命令行工具

命令作用参数备注
jps列出当前机器上正在运行的虚拟机进程,jps 从操作系统的临时目录上去找(所以有一些信息可能显示不全)-q : 仅仅显示进程,
-m: 输出主函数传入的参数
-l: 输出应用程序主类完整 package 名称或 jar 完整名称.
-v: 列出 jvm 参数
jstat监视虚拟机各种运行状态信息,可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT 编译等运行数据
jstat-gc 13616 250 10每 250 ms 查询一次,共 10 次
-class (类加载器)
-compiler (JIT)
-gc (GC 堆状态)
-gccapacity (各区大小)
-gccause (最近一次GC 统计和原因)
-gcnew (新区统计)
-gcnewcapacity (新区大小)
-gcold (老区统计)
-gcoldcapacity (老区大小)
-gcpermcapacity (永久区大小)
-gcutil (GC 统计汇总)
-printcompilation (HotSpot 编译统计)
结果集:
S0C:第一个幸存区(From 区)的大小
S1C:第二个幸存区(To 区)的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园(Eden)区的大小
EU:伊甸园(Eden)区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
jinfo查看和修改虚拟机的参数
jinfo -flag +PrintGC pid修改 manageable 的参数
jinfo –sysprops 可以查看由 System.getProperties() 取得的参数
jinfo –flag 未被显式指定的参数的系统默认值
jinfo –flags(注意s)显示虚拟机的参数
jmap堆信息-heap 打印heap 的概要信息
-histo 打印每个class 的实例数目,内存占用,类全名信息.
–histo:live 只统计活的对象数量.
-dump 生成的堆转储快照
jmap -dump:live,format=b,file=heap.bin
-finalizerinfo 打印正等候回收的对象的信息
用于生成堆转储快照(一般称为 heapdump 或 dump 文件)。jmap 的作用并不仅仅是为了获取 dump 文件,它还可以查询finalize 执行队列、Java 堆和永久代的详细信息,如空间使用率、当前用的是哪种收集器等。和 jinfo 命令一样,jmap 有不少功能在Windows 平台下都是受限的,除了生成dump 文件的
-dump 选项和用于查看每个类的实例、空间占用统计的-histo 选项在所有操作系统都提供之外,其余选项都只能在 Linux/Solaris 下使用。
jhat在服务器上生成堆转储文件分析
jhat dump 文件名
一般不推荐,毕竟占用服务器的资源,比如一个文件就有1 个 G 的话就需要大约吃一个 1G 的内存资源
jstack当前时刻的线程快照,主要是用来排查是否有死锁的情况可以使用 Thread#getAllStackTraces 做管理页面,实现 jstack 的功能
jcmd发送诊断命令请求到正在运行的 JVM

JDK 可视化工具

工具作用备注
JMX管理远程进程
JMX(Java Management Extensions,即 Java 管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架
远程程序的启动参数
-Djava.rmi.server.hostname=……
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8888
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
Jconsole
visualvm
JHSDBJHSDB 是一款基于服务性代理实现的进程外调试工具服务性代理是 HotSpot 虚拟机中一组用于映射 Java 虚拟机运行信息的,主要基于Java 语言实现的 API 集合

压测工具 AB

ab 工具用来测试 post get 接口请求非常便捷,可以通过参数指定请求数、并发数、请求参数等

安装:yum-y install httpd-tools

参数:

参数作用
-n总请求次数(最小默认为 1)
-c并发次数(最小默认为1 且不能大于总请求次数)
-ppost 参数文档路径(-p 和-T 参数要配合使用)
-Theader 头内容类型

示例:

  • get
    • ab -c 10 -n 100 http://www.test.api.com/test/login?userName=test&password=test
  • post
    • ab -n 100 -c 10 -p 'post.txt' -T 'application/x-www-form-urlencoded' 'http://test.api.com/test/register'
    • post.txt 为存放 post 参数的文档
      • usernanme=test&password=test&sex=1

关键性能指标:

指标作用
Requests per second吞吐率,指某个并发用户数下单位时间内处理的请求数;
Time per request用户平均请求等待时间
指处理完成所有请求数所花费的时间/(总请求数/ 并发用户数)
Time per request服务器平均请求处理时间
指处理完成所有请求数所花费的时间/ 总请求数
Percentage of the requests served within a certain time每秒请求时间分布情况,指在整个请求中,每个请求的时间长度的分布情况
例如有 50% 的请求响应在 8ms 内,66% 的请求响应在 10ms 内,说明有 16% 的请求在 8ms~10ms 之间

Arthas

直接 java -jar arthas-boot.jar,选择 attach 的进程绑定

常用命令:

命令作用备注
q退出
dashboard界面 5s 刷新一次
threadthread -i 1000 -n 3每过 1000 毫秒进行采样,显示最占 CPU 时间的前 3 个线程
thread --state WAITING查看处于等待状态的线程
–b 找出阻塞当前线程的线程(排查死锁)
-n 指定最忙的前 n 个线程并打印堆栈
-i 指定 cpu 占比统计的采样间隔,单位为毫秒
jvm查看当前 JVM 信息
jad反编译指定已加载类的源码
trace跟踪统计方法耗时
trace org.jxch.demo.DemoApplication test
monitor统计方法执行情况
monitor -c 5 org.jxch.demo.controller.DemoController test 每 5 秒统计一次 tset 方法执行情况
watch观察方法的入参出参信息
watch org.jxch.demo.controller.DemoController test '{params[0], retrunObj}'
stack输出当前方法被调用的调用路径
tt方法执行数据的时空隧道记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
vmoption查看,更新 JVM 诊断相关的参数
sc查看 JVM 已加载的类信息
sm查看已加载类的方法信息
classloader查看 classloader 的继承树,urls,类加载信息
heapdump类似 jmap 命令的 heap dump 功能

动态追踪技术

其实作为 Java 的动态追踪技术,底层无非就是基于 ASM、Java Attach API、Instrument 开发的创建。cglib、Spring 等框架中对于字节码的操作就建立在 ASM 之上。Arthas 都是针前面这些技术的一个封装而已。

Java Agent

动态追踪技术是一个可以不用重启线上 java 项目来进行问题排查的技术,比如前面讲的 Arthas 就属于一种动态追踪的工具。它里面提供的 monitor 还有 watch 等命令就是动态的追踪技术。
Arthas 工具的基础,就是 Java Agent 技术,可以利用它来构建一个附加的代理程序,用来协助检测性能,还可以替换一些现有功能,甚至 JDK 的一些类我们也能修改,有点像 JVM 级别的 AOP 功能。Java Agent 体现方式是一个 jar 包

在一个 JVM 中,premain/agentmain 只会调用一个

  • premain可以在 main 运行之前的进行一些操作
  • agentmain控制类运行时的行为(Arthas 使用的就是这种)

使用流程

要监控的类:

public class MainRun {
    public static void main(String[] args) { hello("world"); }
    private static void hello(String name) {
        System.out.println("hello" + name);
        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  1. 引入 javassist 依赖
<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.27.0-GA</version>
</dependency>
  1. 创建 AgentApp
// 在一个 JVM 中 premain/agentmain 只会调用一个
public class AgentApp {
    // 可以在 main 运行之前的进行一些操作
    public static void premain(String agentOps, Instrumentation inst){
        System.out.println("premain");
        inst.addTransformer(new AgentTransformer());
    }
    // 控制类运行时的行为(Arthas 使用的就是这种)
    public static void agentmain(String agentOps, Instrumentation inst){
        System.out.println("agentmain");
        inst.addTransformer(new AgentTransformer());
    }
}
  1. 编写 AgentTransformer
public class AgentTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, 
                           Class<?> classBeingRedefined,
                           ProtectionDomain protectionDomain, 
                           byte[] classfileBuffer){
        String loadName = className.replaceAll("/", ".");
        if (className.endsWith("MainRun")) {
            try {
                CtClass ctClass = ClassPool.getDefault().get(loadName);
                CtMethod ctMethod = ctClass.getDeclaredMethod("hello");
                ctMethod.addLocalVariable("_begin", CtClass.longType);
                ctMethod.insertBefore("_begin = System.nanoTime();");
                ctMethod.insertAfter("System.out.println(System.nanoTime()-_begin);");
                return ctClass.toBytecode();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return classfileBuffer;
    }
} 
  1. 配置 MANIFEST.MF

创建 src/main/resources/META-INF/MANIFEST.MF 文件,添加以下内容:

Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
# premain 方式
Premain-Class: xxx.AgentApp
# agentmain 方式
Agent-Class: xxx.AgentApp
  1. 配置 maven
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-jar-plugin</artifactId>
	<configuration>
		<archive>
			<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
		</archive>
	</configuration>
</plugin>
  1. 执行 mvn install 得到 Agent 的 jar 包
  2. 使用
    • premain: 加入启动时参数 java -javaagent:agent.jar MainRun
    • agentmain: 会自动执行 agentmain 方法
      • 使用 Java Attach API

Java Attach API:

jdk/lib/tools.jar 中的工具类中 Java Attach API 是一个 API 接口,它可以将应用程序连接到另一个目标虚拟机。然后,您的应用程序可以将代理应用程序装入目标虚拟机,例如,用于执行监视状态之类的任务。JVM Attach API 功能上非常简单,主要功能如下:

  1. Attach 到其中一个 JVM 上,建立通信管道
  2. 让目标 JVM 加载 Agen
// Java Attach API
public class AttachDemo {
    public static void main(String[] args) throws Exception {
        // VM 进程号,使用 jps 命令可以获取
        // attach 向目标 JVM 附着 Attach 代理工具程序
        VirtualMachine vm = VirtualMachine.attach("508");
        // 获取目标 VM 的系统参数
        Properties props = vm.getSystemProperties();
        String version = props.getProperty("java.version");
        System.out.println(version);
        // VirtualMachineDescriptor 是一个描述虚拟机的容器类,配合 VirtualMachine 类完成各种功能
        List<VirtualMachineDescriptor> vmDescriptors = vm.list();
        // 从 JVM 上面解除代理
        vm.detach();
    }
}
  • VirtualMachine
    • VirtualMachine 代表一个 JVM,也就是程序需要监控的目标虚拟机,提供了 JVM 枚举,Attach 动作和 Detach 动作(Attach 动作的相反行为,从 JVM 上面解除一个代理)等
  • VirtualMachineDescriptor
    • VirtualMachineDescriptor 则是一个描述虚拟机的容器类,配合 VirtualMachine 类完成各种功能。

BTrace

BTrace 是基于 Java 语言的一个安全的、可提供动态追踪服务的工具。BTrace 基于 ASM、Java Attach API、Instrument 开发,为用户提供了很多注解。依靠这些注解,我们可以编写 BTrace 脚本(简单的 Java 代码)达到我们想要的效果,而不必深陷于 ASM 对字节码的操作中不可自拔。

注意:脚本中不能有任何的注释(中文注释)

流程

  1. 下载并配置环境变量 BTRACE_HOME
  2. 创建一个简单的工程,引入三个依赖包:btrace-agent.jar btrace-boot.jar btrace-client.jar
  3. 写 MoreBtrace 类
@Btrace
public class TestBrace {
    @OnMethod(
    	clazz = "xxx.DemoController",
        method = "test",
        location = @Location(Kind.ENTRY)
    )
    public static void checkEntry(
    	@ProbeClassName String pcn,
        @ProbeMethodName String pmn,
        AnyType[] args
    ){
        BTraceUtils.println("Class: " + pcn);
        BTraceUtils.println("Method: " + pmn);
        BTraceUtils.printArray(args);
        BTraceUtils.println("=================");
        BTraceUtils.println();
    }
}
  1. 执行 jps 获得 pid
  2. 命令行执行 btrace pid xxx.java

注解

注解类型作用
@BTrace类注解
@OnMethod方法注解用于指定跟踪方法到目标类,目标方法和目标位置
clazz
method
location
@Location方法注解value,默认值为 Kind.Entry
where,默认值为 Where.BEFORE
clazz
method
field
type
line
@Kind方法注解Kind.Entry方法的开始,方法参数
Kind.Return方法返回值
Kind.ARRAY_SET, Kind.ARRAY_GET数组索引
Kind.FIELD_SET属性值
Kind.NEW类名
@OnTimer方法注解用于指定跟踪操作定时执行。value 用于指定时间间隔
@OnError方法注解当 trace 代码抛异常或者错误时,该注解的方法会被执行
如果同一个 trace 脚本中其他方法抛异常,该注解方法也会被执行
@ProbeClassName参数注解该参数的值就是被跟踪的类名称,需要 @OnMethod
@ProbeMethodName参数注解该参数值是被跟踪方法名称,需要 @OnMethod
@Self参数注解当前截取方法的封闭实例参数
@Return参数注解当前截取方法的的返回值, 只对location=@Location(Kind.RETURN)生效
@Duration参数注解当前截取方法的执行时间
@TargetInstance参数注解当前截取方法内部调用的实例
@TargetMethodOrField参数注解当前截取方法内部被调用的方法名

示例:

  • @OnMethod(clazz="+com.kite.base", method="doSome") + 表示继承
  • @OnMethod(clazz="@javax.jws.WebService", method="@javax.jws.WebMethod") @ 表示注解

Btrace 的限制(安全):虽然修改了字节码,但是除了输出需要的信息外,对整个程序的正常运行并没有影响。BTrace 最终借 Instrument 实现 class 的替换。出于安全考虑,Instrument 在使用上存在诸多的限制

  • 不允许创建对象
  • 不允许创建数组
  • 不允许抛异常
  • 不允许 catch 异常
  • 不允许随意调用其他对象或者类的方法,只允许调用 com.sun.btrace.BTraceUtils 中提供的静态方法(一些数据处理和信息输出工具)
  • 不允许改变类的属性
  • 不允许有成员变量和方法,只允许存在 static public void 方法
  • 不允许有内部类、嵌套类
  • 不允许有同步方法和同步块
  • 不允许有循环
  • 不允许随意继承其他类(当然,java.lang.Object 除外)
  • 不允许实现接口
  • 不允许使用 assert
  • 不允许使用 Class 对象

MAT(分析内存泄漏)

VisualVm 属于比较寒酸的工具,基本上跟 jmap 之类的命令没多少区别,它只是可以事后看,通过 dump 信息来看,里面没有多少可以做分析的功能。

可以通过 jmap 命令把整个堆内存的数据 dump 下来,或者链接正在运行的 JVM。堆快照比较大的话,可以再 MemoryAnalyzer.ini 文件中配置 MAT 初始内存

  • 概要
  • Incoming/Outgoing References
  • 浅堆与深堆:重点关注浅堆比较小但深堆比较大的对象
    • 浅堆(shallow heap):对象本身的内存占用,包括对象自身的内存占用,以及“为了引用”其他对象所占用的内存
      • 非数组类型对象 size = 对象头+各成员变量大小之和(实例数据和父类变量)+对齐填充
      • 数组类型对象 size = shallow size=对象头+类型变量大小(变量不是对象本身)*数组长度+对齐填充
    • 深堆(Retained heap):对象被垃圾回收后,能够释放的内存大小
  • 内存泄漏检测:一定要给线程取名字
  • 支配树视图:定位对象间的引用情况,以及垃圾回收时的引用依赖关系
    • 支配树列出了堆中最大的对象,第二层级的节点表示当被第一层级的节点所引用到的对象,当第一层级对象被回收时,这些对象也将被回收。
    • 支配树视图对数据进行了归类,体现了对象之间的依赖关系。我们通常会根据“深堆”进行倒序排序,可以很容易的看到占用内存比较高的几个对象,点击前面的箭头,即可一层层展开支配关系(依次找深堆明显比浅堆大的对象)。
  • 内存对比:多个快照进行对比来确定增长趋势
  • 线程视图:想要看具体的引用关系,可以通过线程视图
    • 线程在运行中是可以作为 GC Roots 的。我们可以通过线程视图展示了线程内对象的引用关系,以及方法调用关系,相对比 jstack 获取的栈 dump,我们能够更加清晰地看到内存中具体的数据。
  • 柱状图视图:可以看到除了对象的大小,还有类的实例个数
  • Path To GC Roots: 从一个对象到 GC Roots 的引用链被称为 Path to GC Roots
    • 可以找出 JAVA 的内存泄露问题,当程序不在访问该对象时仍存在到该对象的引用路径(这个对象可能内存泄漏)。
    • 再次选择某个引用关系,然后选择菜单“Path To GC Roots”,即可显示到 GC Roots 的全路径。通常在排查内存泄漏的时候,会选择排除虚弱软等引用。
  • OQL:类似于 SQL 的查询语言 OQL(Object Query Language)
    • select * from ex14.ObjectsMAT$A查询 A 对象
    • select * from java.lang.String s wheretoString(s) like".*java.*"查询包含 java 字样的所有字符串
    • 文档

GC 日志分析

[GC (GC 原因) [代: 回收前代空间 -> 回收后代空间(代总大小)] 回收前堆空间 -> 回收后堆空间(堆总空间), 耗时 secs]
[Tims: user=用户时间 sys=内核时间, real=总计时间 secs]

  • GChisto
  • gcviewer
  • Gceasy
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值