`
beck5859509
  • 浏览: 110538 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

ASM

 
阅读更多
/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"于公司jvm智能分析平台的思考

引子
在讲述主题之前,先看一下之前遇到的几个问题
1、问题一
2、问题二
3、问题三
url1
url2
url3


目前公司缺少一个jvm智能分析平台,在遇到一些比较难定位的问题时,往往只能查看日志,或者临时添加日志,但也会带来一些麻烦,比如日志量过大,进程重启问题不必现等,又或者只能反复jstack进行问题猜测,但效果也不理想。

jvm智能分析平台的特点。
1、动态加载,目标程序无需重启
2、字节码编程、性能得到保障
3、web平台化操作
3、目标方法的入参、异常、返回结果的分析
4、目标方法内部每一行代码的hao时的分析
5、指定java实例数统计及size分析
3、与第三方jar进行隔离,代码无污染
4、对线程栈做快照对线程

demo展示

冰山一角

可行性
已和运维组勇哥沟通,并无相关的平台,只是简单的组件的监控

相关技术
/java/jni/jvmti/bytecode/c/c++



1、统计指定的java实例数,并可以统计每个实例的大小(size)
2、查找最高耗时的方法(小型程序)
3、拉取git代码进行web在线调试
4、采集到与JVM 有关的大量信息,例如内存、线程、JVM 崩溃等
5、对线程栈做快照
6、Java Agent受jvm本身的影响

总结
Native Agent因为工作原理的不同,导致其与Java Agent相比,拥有明显的优势,具体总结如下:
1. 获取JVM运行时的性能参数。
2. 获取JVM线程方法调用栈信息
3. 不受JVM的运行状态影响。
4. 开销更少

visitAnnotationDefault?
( visitAnnotation | visitParameterAnnotation | visitAttribute )*
( visitCode
( visitTryCatchBlock | visitLabel | visitFrame | visitXxxInsn |
visitLocalVariable | visitLineNumber )*
visitMaxs )?
visitEnd


Btrace (Byte Trace)是sun推出的一款Java 动态、安全追踪(监控)工具,可以在不停机的情况下监控系统运行情况,并且做到最少的侵入,占用最少的系统资源。。BTrace在使用上做了很多限制,如不能创建对象、不能使用数组、不能抛出或捕获异常、不能使用循环、不能使用synchronized关键字、脚本的属性和方法都必须使用static修饰等,具体限制条件可参考用户手册。根据官方声明,不当地使用BTrace可能导致JVM崩溃,如BTrace使用错误的.class文件,所以,可以先在本地验证BTrace脚本的正确性再使用



https://blog.csdn.net/coslay/article/details/43113029
https://www.cnblogs.com/163yun/p/10078137.html

asm:
0:   aload_0
首先第一个0代表虚指令中的行号(后面会应到,确切说应该是方法的body部分第几个字节),每个方法从0开始顺序递增,但是可以跳跃,跳跃的原因在于一些指令还会接操作的内容,这些操作的内容可能来自常量池,也可以标志是第几个slot的本地变量,因此需要占用一定的空间。

aload_0指令是将“第1个”slot所在的本地变量推到栈顶,并且这个本地变量是引用类型的,相关的指令有:aload_[0-3](范围是:0x2a ~ 0x2d)。如果超过4个,则会使用“aload + 本地变量的slot位置”来完成(此时会多占用1个字节来存放),前者是通过具体的几个指令直接完成。

许多地方会解释为第1个引用类型的本地变量,但胖哥是一个逻辑怪,认为这句话有问题,并不是第1个引用变量,普通变量如果在它之前,它也不是第1个了,此时本身就是第1个本地变量,更确切地说是第一个slot所在位置的本地变量。



注意:

常量池的存放内容

存放所有的方法名

field名

方法签名(方法参数+返回值)

类型名

class文件中的常量值

常量池的前四部分可以称作是符号引用(即只有一些名称,但没有实际的地址,在运行期进行类的加载过后,会为这些东西分配实际的内存,到时候符号引用就会转化为直接引用,就能被JVM用了)

常量池的组成:符号引用、常量(这个常量包含我们代码中定义的常量,eg、字符串常量,也包括class文件中的常量,eg.SourceFile)。

主版本号的对应(eg.50对应jdk6,51对应jdk7),查看《深入理解java虚拟机(第二版)》P167

Stack:操作数栈的深度(这个值就是类加载阶段为操作数栈分配的深度)

Locals:局部变量的分配空间(单位是slot,不是个数),对于double和long这两个64bit的,需要两个slot,对于其他<=32bit的,只需要一个slot

Args_size:方法参数的个数,包括方法参数、this(this只针对实例方法,static方法不会自动添加this)

inc()方法:我详细注释了该方法的执行过程,这也就是JVM执行一个方法的基本流程(基于栈)


ClassReader.java

        // visits the class declaration
        classVisitor.visit(readInt(items[1] - 7), access, name, signature,
                superClass, interfaces);

        // visits the source and debug info
        if ((flags & SKIP_DEBUG) == 0
                && (sourceFile != null || sourceDebug != null)) {
            classVisitor.visitSource(sourceFile, sourceDebug);
        }

        // visits the module info and associated attributes
        if (module != 0) {
            readModule(classVisitor, context, module,
                    moduleMainClass, packages);
        }
       
        // visits the outer class
        if (enclosingOwner != null) {
            classVisitor.visitOuterClass(enclosingOwner, enclosingName,
                    enclosingDesc);
        }

        // visits the class annotations and type annotations
        if (anns != 0) {
            for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
                v = readAnnotationValues(v + 2, c, true,
                        classVisitor.visitAnnotation(readUTF8(v, c), true));
            }
        }
        if (ianns != 0) {
            for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
                v = readAnnotationValues(v + 2, c, true,
                        classVisitor.visitAnnotation(readUTF8(v, c), false));
            }
        }
        if (tanns != 0) {
            for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
                v = readAnnotationTarget(context, v);
                v = readAnnotationValues(v + 2, c, true,
                        classVisitor.visitTypeAnnotation(context.typeRef,
                                context.typePath, readUTF8(v, c), true));
            }
        }
        if (itanns != 0) {
            for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
                v = readAnnotationTarget(context, v);
                v = readAnnotationValues(v + 2, c, true,
                        classVisitor.visitTypeAnnotation(context.typeRef,
                                context.typePath, readUTF8(v, c), false));
            }
        }

        // visits the attributes
        while (attributes != null) {
            Attribute attr = attributes.next;
            attributes.next = null;
            classVisitor.visitAttribute(attributes);
            attributes = attr;
        }

        // visits the inner classes
        if (innerClasses != 0) {
            int v = innerClasses + 2;
            for (int i = readUnsignedShort(innerClasses); i > 0; --i) {
                classVisitor.visitInnerClass(readClass(v, c),
                        readClass(v + 2, c), readUTF8(v + 4, c),
                        readUnsignedShort(v + 6));
                v += 8;
            }
        }

        // visits the fields and methods
        u = header + 10 + 2 * interfaces.length;
        for (int i = readUnsignedShort(u - 2); i > 0; --i) {
            u = readField(classVisitor, context, u);
        }
        u += 2;
        for (int i = readUnsignedShort(u - 2); i > 0; --i) {
            u = readMethod(classVisitor, context, u);
        }

        // visits the end of the class
        classVisitor.visitEnd();



























引子



    在正式讲述本文主题前,先看一下之前项目中遇到的几个问题



    问题一

        问题现象:18年12月底的时候线上有一台手机在 18:00~21:00之间频繁连接和断开连接

        分析步骤:

        1、客户端同事初步分析手机日志,发现有断开异常,看调用堆栈是业务代码抛出

        image.png



        2、联想到后台主动关闭连接时,不会写入mqtt 消息,所以输入流stream读不到数据属于正常现象。

             另外问题发生时的日志已经被清除,只能查看客户端的其它日志,幸运的是当时有网络包可以分析

        3、查看网络请求终于发现一些端倪:

            image.png

            image.png

            客户端先是连接101.132.150.36,同时后台139.196.100.216断开客户端连接(18:00:53前的连接),18:01分客户端又继续连接139.196.100.216,形成循环连接的现象。

            重点查看连接connack的返回结果,找到了突破口。

            image.png

         4、第二个红框值为3,代表需要重新注册。因个别手机在多用户场景会出现连接切换重连的情况,后台发现token不匹配就会进行重连,客户端已经针对此情况修复。



    问题二

        问题现象:5月初收到ups-receiver线程池任务队列阻塞的监控报警

        分析步骤:

        1、查看错误日志,并没有找到相应的线索,也没有异常的日志。

        2、因为当天凌晨12点半发现,临时重启解决问题。

        3、对比内外部的receiver,单独拆分了回执线程池处理,并发布上线观察。

        4、果然第二天问题还是出现,打印jstack进行分析。

        5、问题得到确认,回执写入mq时的线程被阻塞,联系中间件同事进行处理,问题得以解决。

            image.png



    其它烧脑问题

    http://km.vivo.xyz/pages/viewpage.action?pageId=28500755

    http://km.vivo.xyz/pages/viewpage.action?pageId=16279250

    http://km.vivo.xyz/pages/viewpage.action?pageId=14488732



    当前痛点:

     梳理以上问题时都存在一个共同特点,业务在遇到一些比较难定位的问题时,往往只能查看日志,或者临时添加日志,但这也会带来一些麻烦,比如日志量过大,

     进程重启问题不必现,需要临时发布版本(可能引入其它风险)等,或者只能反复jstack进行问题猜测,但效果都不理想。当然针对不同层次需求场景,有对应

     的工具,比如内存占用过多,得不到释放,可以dump出文件,但是分析此文件也并不容易,有一定的成本。还有类似jconsole等工具,但已有的这些工具并不能

     满足高级的应用场景,比如动态查看方法的入参/返回结果/某个成员变量的值就可以很快定位出问题。



     解决方案:

        一个比较好的解决方案是建设一个公司级的JVM智能分析平台 --- 开发人员可以根据不同场景输入参数快速定位问题,提前监测,并协助智能分析问题发生的根因。

        jvm智能分析平台具备以下特点。

        1、动态加载,目标程序无需重启

        2、字节码编程、性能得到保障

        3、web平台化操作

        4、对目标方法的入参、异常、返回结果的分析

        5、对目标方法内部代码的耗时的分析

        6、对指定java实例数统计及size分析

        7、与第三方jar进行隔离,代码无污染

        8、对线程堆栈智能分析可能的问题原因



   冰山一角

    jdk1.5以后,jdk整合了一套有关虚拟机的JVMTI接口,可以供使用者调用,JVMTI的功能非常丰富,包含了访问虚拟机中线程/内存/堆/栈/类/方法/变量的方法。

    开发者可以灵活使用这些特性,开发出很多高级功能,比如上述提到的功能,不过这些接口是C/C++的,实现起来有一定的难度。以下例举两个例子。



    例一:

    比如下面展示了一个比较有趣的例子---通过java类名查找进程中对应实例的数量,具本如下。

    1、java端的统计调用功能由一个本地方法实现,通过jni的方式嵌入jvm中,具体实现通过c++代码去实现

    image.png

  

    2、监控程序启动时开启jvm统计对象数的功能

    image.png

    3、使用jvmit的方法迭代对象size及实例个数

    image.png

    4、最终结果,已之前java代码预期一致

    image.png



    除了通过jvmti实现上述功能(实现成本偏高),jdk还提供了instrument技术,可通过字节码转换进行实现相同的功能,并且功能更强大,

    除了实现监测,还可以动态添加新的功能:)



    例二:

    这里例举给目标方法动态添加try/catch及一个局部变量int i = 20的例子,当然也可以增加耗时统计

    1、Student类中最初的say方法

    image.png

    编译后Student的say方法字节码:

    image.png

    2、对目标方法进行添加字节码(部分代码)

    image.png

    3、编译后say方法的字节码

    image.png

   

    查看上图汇编后的字节码文件,istore_1已经将栈顶的int值20保存到了局部变量中,并且增加了try/catch的异常捕获

    总结:字节码编程方式同样可以动态实现目标程序的变更,但底层也通过jvmti进行实现的,但是通过字节码简单化了jvmti的开发工作量,当然为了更好的理解

              是有必要同时掌握jvmti指令及字节码指定的。以上只是字节码编程的冰山一角,这里不展开介绍了。



    以下是 jvm智能分析平台架构图



   image.png   



    该系统后期还可以衍生其它更强大的功能:比如通过web端拉取git代码在线调试线上环境(支付宝目前已有类似的功能)

 

    成本:

        关于系统人力评估(正式加两个外包-前端和后端各一人)

    收益 :

        1、智能分析系统,提前发现疑难、隐藏较深的问题,减少线上问题造成的损失。

        2、节省定位问题成本,现在项目越来越多,系统逻辑也越来越复杂,通过该系统快速锁定问题原因。
分享到:
评论

相关推荐

    asm.jar各个版本

    asm-1.3.3.jar, asm-1.3.4.jar, asm-1.3.5.jar, asm-1.4.1.jar, asm-1.4.2.jar, asm-1.4.3.jar, asm-1.4.jar, asm-1.5.1.jar, asm-1.5.2.jar, asm-1.5.3.jar, asm-2.0.jar, asm-2.1.jar, asm-2.2.1-sources.jar, asm...

    1、ASM1064 DATASHEET; 2、ASM1064 参考原理图设计; 3、支持的SPI Flash清单

    ASM1064是一款高性能的PCI Express to SATA 3.0桥接芯片,由ASMIC公司设计制造。这款芯片主要用于实现PCI Express接口与SATA接口之间的数据高速传输,广泛应用于存储扩展卡、固态硬盘控制器等领域。以下是关于ASM...

    ASM 1351.zip

    ASM 1351 是一款由ASMedia( ASM 微电子)公司开发的集成电路,主要应用于数据传输和接口控制领域。这个压缩包“ASM 1351.zip”包含了与ASM 1351相关的三个关键文件:一个固件升级工具、数据表以及设计套件。 1. **...

    redhat/centos6.9 kmod-oracleasm/oracleasm-support/oracleasm rpm包

    kmod-oracleasm-2.0.8-15.el6_9.x86_64 oracleasm-support-2.1.8-1.el6.x86_64 oracleasmlib-2.0.4-1.el6.x86_64 安装顺序: rpm -ivh kmod-oracleasm-2.0.8-15.el6_9.x86_64.rpm rpm -ivh oracleasm-support-2.1.8...

    cglib-2.2.jar asm-tree.jar asm-commons.jar asm.jar

    【标题】"cglib-2.2.jar asm-tree.jar asm-commons.jar asm.jar" 提供的是一组用于Java编程的库,它们主要用于实现动态代理和字节码操作。 【描述】"cglib动态代理模式jar包 cglib-2.2.jar asm-tree.jar asm-...

    EditPlus(附asm.acp,asm.stx)

    ASM文件的扩展名通常为`.asm`,它允许程序员对计算机硬件进行精确控制,尽管相比高级语言,学习曲线较为陡峭,但在某些特定场景,如系统编程或优化代码时,汇编语言显得尤为重要。 【asm.acp】和【asm.stx】是...

    asm 6.0 工具集

    这套工具包括了ASM、ASM-Util、ASM-TREE和ASM-ANALYSIS等组件,每个都有其特定的功能和用途。 **ASM库**是核心部分,提供低级别的API来生成和解析Java字节码。ASM库允许开发者直接操作字节码,创建和修改类,甚至在...

    asm-util.jar

    asm-util-1.3.4.jar, asm-util-1.3.5.jar, asm-util-1.4.1.jar, asm-util-1.4.3.jar, asm-util-1.5.1.jar, asm-util-1.5.2.jar, asm-util-1.5.3.jar, asm-util-2.0.jar, asm-util-2.1.jar, asm-util-2.2.1-sources....

    各种oracleasm rpm包(Linux下配置ASM使用)

    包含如下oracleasm包: kmod-oracleasm-2.0.6.rh1-3.el6.x86_64.rpm oracleasm-2.0.8-4.el6_6.src.rpm oracleasm-2.0.8-6.el6_7.src.rpm oracleasm-2.0.8-8.el7.src.rpm oracleasm-2.0.8-15.el7.centos.src.rpm ...

    asm操作指南(中文)

    ### asm操作指南(中文)知识点总结 #### 一、ASM框架简介 - **定义与功能**:ASM是一个Java字节码操纵框架,主要用于动态生成类或增强现有类的功能。通过直接生成二进制`.class`文件,ASM能够在类被加载到Java...

    ASM1061.zip

    ASM1061是一款由ASMtek(Asmedia Technology Inc.)公司生产的高性能PCI Express (PCIe) to Serial Advanced Technology Attachment (SATA)桥接芯片,主要用于扩展计算机系统的SATA接口,使得硬件开发人员能够轻松地...

    stm32duino/ASM330LHH

    在本案例中,我们关注的是ASM330LHH,这是一款高性能的三轴加速度计和三轴陀螺仪传感器,由意法半导体(STMicroelectronics)制造。ASM330LHH广泛应用于运动检测、航姿参考系统、手势识别以及物联网设备中的动态平衡...

    ASM1083 PCIe转PCI芯片数据表

    ASM1083 PCIe转PCI芯片数据表 ASM1083 PCIe转PCI芯片数据表是ASMedia TECHNOLOGY INC.公司出品的一款PCIe转PCI桥接芯片,其主要功能是将PCI Express(Peripheral Component Interconnect Express)接口转换为传统的...

    C-include-ASM.zip_asm中include asm_c语言中嵌套asm

    在标题提到的"C-include-ASM.zip_asm中include_asm_c语言中嵌套asm",我们主要讨论的是如何在C程序中使用汇编代码,并且可能涉及到如何在汇编代码中包含其他汇编模块。 首先,让我们了解一下C语言嵌套汇编的基本...

    asm 最新版手册

    ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class ...

    汇编插件 asm-dude, 支持 Visual Studio 2022

    **汇编插件 AsmDude 用于 Visual Studio 2022** AsmDude 是一款专为Visual Studio 2022设计的汇编语言插件,它极大地提升了开发人员在使用汇编语言时的工作效率和代码质量。这款插件对新手极其友好,即使是编程经验...

    ASM1061资料文件.rar

    ASM1061是一款广泛应用在PCI-E到SATA转换中的控制器芯片,由ASMtek公司生产。这个"ASM1061资料文件.rar"压缩包包含了关于该芯片的详细设计资料,对于开发者、工程师或者硬件爱好者来说是极其宝贵的资源。下面我们将...

    oracle不使用oracleasm的包配置ASM磁盘配置方法

    ### Oracle 不使用 OracleASM 的包配置 ASM 磁盘配置方法 #### 概述 在 Oracle 数据库系统中,自动存储管理(ASM)是用于管理数据库文件的一种高性能、高可用性的解决方案。通常情况下,ASM 依赖于 Oracle 提供的 ...

    asm330lhh1_datasheet_gyroscope_asm330lhh精度_

    《ASM330LHH1陀螺仪:高精度数据手册解析》 ASM330LHH1是一款高性能的固态陀螺仪,主要用于需要精确测量角速度的应用中。这款陀螺仪以其卓越的精度和稳定性,广泛应用于无人机、机器人导航、虚拟现实设备以及各类...

    ASM4手册中文版.pdf.zip

    ASM4是中国Java开发者常用的一款字节码操作框架ASM的第四个主要版本,它主要用于动态生成和分析Java字节码。ASM是一个低级别的库,可以直接操作和生成类的字节码,这在创建编译器、代码分析工具以及运行时代码修改等...

Global site tag (gtag.js) - Google Analytics