• java-性能排查工具


    性能排查工具:

    第三方:arthas使用和原理以及btrace、housemd、greys等性能排查工具、在线gc分析工具(gceasy)

    jdk:

    jconsole、jvisualvm、jmc

    参考:

    https://arthas.aliyun.com/doc/

    https://www.cnblogs.com/fengzheng/p/7416942.html

    https://github.com/oldmanpushcart/greys-anatomy

    https://github.com/CSUG/HouseMD

    https://gceasy.io/

    https://blog.csdn.net/qq_40180411/article/details/103893219

    Arthas
    1.安装
    //下载jar包
    sudo curl -O https://arthas.aliyun.com/arthas-boot.jar

    //运行jar包
    sudo -u tomcat java -jar arthas-boot.jar

    2.常用命令
    dashboard:jvm实时面板
    -i:刷新实时数据间隔时间
    -n:刷新实时数据的次数

    ID: Java级别的线程ID,注意这个ID不能跟jstack中的nativeID一一对应。

    NAME: 线程名

    GROUP: 线程组名

    PRIORITY: 线程优先级, 1~10之间的数字,越大表示优先级越高

    STATE: 线程的状态

    RUNNABLE 线程运行中或I/O等待
    BLOCKED 线程在等待monitor锁(synchronized关键字)
    TIMED_WAITING 线程在等待唤醒,但设置了时限
    WAITING 线程在无限等待唤醒
    CPU%: 线程的cpu使用率。比如采样间隔1000ms,某个线程的增量cpu时间为100ms,则cpu使用率=100/1000=10%

    DELTA_TIME: 上次采样之后线程运行增量CPU时间,数据格式为秒

    TIME: 线程运行总CPU时间,数据格式为分:秒

    INTERRUPTED: 线程当前的中断位状态

    DAEMON: 是否是daemon线程

    heap:堆
    nonheap:方法区(类信息,常量(常量池),静态变量)
    metaspace:类的元数据存储
    thead:线程信息
    -n:打印n个耗时最多的线程
    -b:打印阻塞其他线程的线程
    sc(search-class):查找类 可以通配符找到全限定名
    monitor:方法耗时监控
    -c:统计周期(默认120s)

    watch:查看出入参数
    -n:执行次数

    3.原理
    Instrument和Attach API
    Jdk5增加了一个包java.lang.instrument,提供了对Jvm底层组件的访问能力,Instrument要求在运行前利用命令行参数或者系统参数设置代理类,VM启动完成之后(绝大多数类加载前)初始化。

    开发基于instrument的应用,需要这么几个步骤:

    编写premain函数
    jar文件打包,制定Premain-Class
    使用-javaagent参数启动

    jdk5只有在main运行之前进行访问

    Jdk6以后,针对这点进行了改进,开发者可以在main函数执行之后再启动自己的Instrument应用,入口是agentmain函数。arthas就是通过这个实现的。

    之后就可以通过addTransformer,retransformClasses,redefineClasses等方式对字节码进行增强和热替换了。

    ASM

    ASM是一个Java字节码操作框架,用来动态生成class或者增强class,cglib的底层就是它,arthas也是通过它实现对class的增强的。通过AOP实现

    cglib : ASM框架对字节码文件进行修改,主要适用于类
    jdk动态代理 :使用反射生成一个代理接口的匿名类,主要适用于接口的实现类。(因为生成的代理类继承了proxy.java)

    JPDA
    https://juejin.cn/post/6844903608522113038

    JVMTI又是在JPDA(Java Platform Debugger Architecture)之下的三层架构之一,JVMTI(JVM Tool Interface)、JDWP(Java Debug Wire Protocol)、JDI(Java Debug Interface)

    周方鹏 > 性能排查工具 > 1595409-20190921212320818-499259713.png

    IDEA的debug也就是使用jpda架构的功能来实现的

    agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8080,其实就是加载了jdwp的lib,开启了调试端口,然后就可以通过JDI接口进行交互调试。
    Demo

    
    https://github.com/dengshiwei/asm-module
    
    package Demo;
    
    import jdk.internal.org.objectweb.asm.*;
    import jdk.internal.org.objectweb.asm.commons.AdviceAdapter;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Method;
    
    public class CostTime {
    
        public static void main(String[] args) {
            redefinePersonClass();
        }
    
        private static void redefinePersonClass() {
            String className = "Demo.AsmDemo";
            try {
                InputStream inputStream = new FileInputStream("C:\\Users\\fangpengx.zhou\\Desktop\\test\\out\\production\\test\\Demo\\AsmDemo.class");
                // 1. 创建 ClassReader 读入 .class 文件到内存中
                ClassReader reader = new ClassReader(inputStream);
                // 2. 创建 ClassWriter 对象,将操作之后的字节码的字节数组回写
                ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
                // 3. 创建自定义的 ClassVisitor 对象
                ClassVisitor change = new ChangeVisitor(writer);
                // 4. 将 ClassVisitor 对象传入 ClassReader 中
                reader.accept(change, ClassReader.EXPAND_FRAMES);
    
                Class clazz = new MyClassLoader().defineClasses(className, writer.toByteArray());
                Object personObj = clazz.newInstance();
                Method nameMethod = clazz.getDeclaredMethod("printHelloWorld", null);
                nameMethod.invoke(personObj, null);
                // 获取修改后的 class 文件对应的字节数组
                System.out.println("Success!");
                byte[] code = writer.toByteArray();
                try {
                    // 将二进制流写到本地磁盘上
                    FileOutputStream fos = new FileOutputStream("C:\\Users\\fangpengx.zhou\\Desktop\\HelloWorld.class");
                    fos.write(code);
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("Failure!");
            }
        }
    
        static class ChangeVisitor extends ClassVisitor {
    
            ChangeVisitor(ClassVisitor classVisitor) {
                super(Opcodes.ASM5, classVisitor);
            }
    
            @Override
            public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
                MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions);
                if (name.equals("<init>")) {
                    return methodVisitor;
                }
                return new ChangeAdapter(Opcodes.ASM4, methodVisitor, access, name, desc);
            }
        }
    
        static class ChangeAdapter extends AdviceAdapter {
            private int startTimeId = -1;
    
            private String methodName = null;
    
            ChangeAdapter(int api, MethodVisitor mv, int access, String name, String desc) {
                super(api, mv, access, name, desc);
                methodName = name;
            }
    
            @Override
            protected void onMethodEnter() {
                super.onMethodEnter();
                startTimeId = newLocal(Type.LONG_TYPE);
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
                mv.visitIntInsn(LSTORE, startTimeId);
            }
    
            @Override
            protected void onMethodExit(int opcode) {
                super.onMethodExit(opcode);
                int durationId = newLocal(Type.LONG_TYPE);
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
                mv.visitVarInsn(LLOAD, startTimeId);
                mv.visitInsn(LSUB);
                mv.visitVarInsn(LSTORE, durationId);
                mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
                mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
                mv.visitInsn(DUP);
                mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
                mv.visitLdcInsn("The cost time of " + methodName + " is ");
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
                mv.visitVarInsn(LLOAD, durationId);
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false);
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
            }
        }
    
    }
    
    
    
    
    
    
    
    
    
    
    //结果
    "C:\Program Files\Java\jdk1.8.0_261\bin\java.exe" "-javaagent:C:\Users\fangpengx.zhou\IntelliJ IDEA 2020.1.2\lib\idea_rt.jar=14144:C:\Users\fangpengx.zhou\IntelliJ IDEA 2020.1.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_261\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\rt.jar;C:\Users\fangpengx.zhou\Desktop\test\out\production\test" Demo.CostTime
    hello world
    The cost time of printHelloWorld is 1001
    Success!
    
    Process finished with exit code 0
    
    
    
    //反编译
    package Demo;
    
    public class AsmDemo {
        public AsmDemo() {
        }
    
        public void printHelloWorld() {
            long var1 = System.currentTimeMillis();
    
            try {
                String s = "hello world";
                Thread.sleep(1000L);
                System.out.println(s);
            } catch (Exception var6) {
                var6.printStackTrace();
            }
    
            long var4 = System.currentTimeMillis() - var1;
            System.out.println("The cost time of printHelloWorld is " + var4);
        }
    }
    
    
    
    
    
    
    
    
    
    //查看线程信息
    package Demo.test;
    
    import java.lang.management.ManagementFactory;
    import java.lang.management.MemoryUsage;
    import java.lang.management.ThreadInfo;
    import java.lang.management.ThreadMXBean;
    
    public class MultiThread {
    
        public static void main(String[] args){
            //获取java的线程管理MXBean
            ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
            //不需要获取同步的monitor和synchronizer信息,仅获取线程和线程堆栈信息
            ThreadInfo[] threadInfo = threadBean.dumpAllThreads(false, false);
            //遍历线程信息,仅打印线程id和线程名称信息
            for(ThreadInfo info : threadInfo){
                System.out.println(info.getThreadId() + "--" + info.getThreadName() +"--"+ info.getThreadState().name());
            }
    
           //jvm内存MXbEAN
            MemoryUsage heapMemoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
            System.out.println("jvm.heap.init is " + (heapMemoryUsage.getInit()));
            System.out.println("jvm.heap.used is " + (heapMemoryUsage.getUsed()));
            System.out.println("jvm.heap.committed is " + (heapMemoryUsage.getCommitted()));
            System.out.println("jvm.heap.max is " + (heapMemoryUsage.getMax()));
        }
    
    }
    
    
    //结果
    "C:\Program Files\Java\jdk1.8.0_261\bin\java.exe" "-javaagent:C:\Users\fangpengx.zhou\IntelliJ IDEA 2020.1.2\lib\idea_rt.jar=2050:C:\Users\fangpengx.zhou\IntelliJ IDEA 2020.1.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_261\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\rt.jar;C:\Users\fangpengx.zhou\Desktop\test\out\production\test" Demo.test.MultiThread
    6--Monitor Ctrl-Break--RUNNABLE
    5--Attach Listener--RUNNABLE
    4--Signal Dispatcher--RUNNABLE
    3--Finalizer--WAITING
    2--Reference Handler--WAITING
    1--main--RUNNABLE
    jvm.heap.init is 268435456
    jvm.heap.used is 5381192
    jvm.heap.committed is 257425408
    jvm.heap.max is 3797417984
    
    Process finished with exit code 0
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
  • 相关阅读:
    维基百科文章爬虫和聚类【二】:KMeans
    Unity UGUI的Toggle(复选框)组件的介绍及使用
    【面试题】15个提高Javascript开发技巧
    微服务从代码到k8s部署应有尽有系列(十四、部署环境搭建)
    LeetCode50天刷题计划(Day 41 —颜色分类(13.00-14.10)
    Day6-创造[哈希表]:array,set,map
    uni-app 介绍及使用
    经典问题--超大字符串型整数加减
    Qtreeview改变当前节点的名称
    二叉树的递归问题
  • 原文地址:https://blog.csdn.net/weixin_44464373/article/details/125430632