Fastjson源码分析—ASM的作用和实现(1)

2021SC@SDUSC
众所周知,所有json解析工具中,Fastjson速度是最快的。而它能够快速对json字符串进行处理的原因之一,便是它使用了ASM框架。

ASM是什么

首先,我们把关注点放在ASM的定义上。

ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

概括地说,ASM是一个能够不通过.java文件而直接修改.class文件的字节码操控框架。
Fastjson之所以速度快,原因之一是它使用了ASM。按照通常思路,反序列化应该是反射调用set方法进行属性设置。 这种方法是最简单的,但也是最低效的。而Fastjson使用ASM自己编写字节码,然后通过ClassLoader将字节码加载成类,避免了反射开销,大大增强了性能。

字节码格式

在此之前,了解一下Fastjson中ASM部分代码所在位置。
在这里插入图片描述

com.alibaba.fastjson.asm包中就是asm的全部代码。

要了解ASM的工作方式,先得了解字节码文件内部的格式。

Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符,Class文件中存储的内容几乎全部是程序运行的必要数据,没有空隙存在。当遇到需要占用8位字节以上空间的数据项时,就按照高位在前的方式分割成若干个8位字节进行存储。
Class文件格式采用类似于C语言结构体的伪结构来存储数据,这种伪结构只有两种数据类型:无符号数和表。

使用hexdump -C指令查看class文件的二进制代码
在这里插入图片描述

这里我们重点关注三个部分:魔数、版本号和常量池

魔数

class文件前8字节为魔数magic number,可以看到文件的前8位为cafe babe,正表示这是一个class类型的文件。

版本号

接下来的8字节是该文件的版本号,其中前4字节是次版本号minor version(00 00),后4字节是主版本号major version(00 34)。

常量池

接下来是常量池的入口,前4字节表示常量池容量,我这里是00 21,表示十进制的33,由于常量池计数从1开始,所以实际上常量的数量为32个。
常量池主要存储了字面量以及符号引用,其中字面量主要包括字符串,final常量的值或者某个属性的初始值等,而符号引用主要存储类和接口的全限定名称,字段的名称以及描述符,方法的名称以及描述符。

访问标志及其他

常量池之下是访问标志、字段表等class文件的其他信息,这里暂不一一表述。

通过命令行进入项目的target文件夹,找到编译后的class文件,执行指令javap -v Student.class对class文件反编译查看class文件结构。
这里截取一部分内容
在这里插入图片描述
可以看到反编译出来的class文件对每个常量进行了编号,一共32个常量。

ASM如何生成class文件

ASM使用访问者模式,动态对Java类进行修改。
在 ASM 中,提供了一个 ClassReader类,这个类可以直接由字节数组或由 class 文件间接的获得字节码数据,它能正确的分析字节码,构建出抽象的树在内存中表示字节码。它会调用 accept方法,这个方法接受一个实现了 ClassVisitor接口的对象实例作为参数,然后依次调用 ClassVisitor接口的各个方法。

 public void accept(TypeCollector classVisitor) {
        char[] c = new char[this.maxStringLength];
        int anns = false;
        int ianns = false;
        int u = this.header;
        int v = this.items[this.readUnsignedShort(u + 4)];
        int len = this.readUnsignedShort(u + 6);
        int w = false;
        u += 8;
        int i;
        for(i = 0; i < len; ++i) {
            u += 2;
        }
        i = this.readUnsignedShort(u);
        int j;
        for(v = u + 2; i > 0; --i) {
            j = this.readUnsignedShort(v + 6);

            for(v += 8; j > 0; --j) {
                v += 6 + this.readInt(v + 2);
            }
        }

        i = this.readUnsignedShort(v);

        for(v += 2; i > 0; --i) {
            j = this.readUnsignedShort(v + 6);

            for(v += 8; j > 0; --j) {
                v += 6 + this.readInt(v + 2);
            }
        }

        i = this.readUnsignedShort(v);

        for(v += 2; i > 0; --i) {
            v += 6 + this.readInt(v + 2);
        }

        i = this.readUnsignedShort(u);

        for(u += 2; i > 0; --i) {
            j = this.readUnsignedShort(u + 6);

            for(u += 8; j > 0; --j) {
                u += 6 + this.readInt(u + 2);
            }
        }

        i = this.readUnsignedShort(u);
		//依次调用class Visitor的各个方法
        for(u += 2; i > 0; --i) {
            u = this.readMethod(classVisitor, c, u);
        }

    }

ClassReader知道如何对visitor的方法进行调用,而这个过程中用户无法干预。各个 ClassVisitor通过职责链模式,可以非常简单的封装对字节码的各种修改,而无须关注字节码的字节偏移,因为这些实现细节对于用户都被隐藏了,用户要做的只是重写相应的 visit 函数。
标准版的ASM包里面会有ClassAdaptor类,实现了Class Visitor的所有方法。但是Fastjson使用的是阉割版的ASM,只保留了必要的内容,降低了程序大小。Fastjson的ASM包里面只有一千余行代码。

ASM中另一个关键的类是ClassWriter,负责写字节码文件,以字节数组的形式输出。

ASM处理字节码的流程如下:

  1. 创建ClassReader对象,将.class文件的内容读入到字节数组中
  2. 创建ClassWriter对象,将处理后的字节数组回写入.class文件
  3. 使用时间过滤器ClassVisitor,对相应的方法修改并返回

ASM在Fastjson中的开启/关闭

ParserConfig.getGlobalInstance().setAsmEnable()方法来设置ASM的开启和关闭。
我们分别测试一下ASM开启和关闭时的性能,测试代码如下:

public void test02(){
        String str="{\"age\":13,\"name\":\"james\"}";
        boolean flag;
        flag=true;
        ParserConfig config=ParserConfig.getGlobalInstance();
        config.setAsmEnable(flag);
        long t1=System.currentTimeMillis();
        Student student=JSON.parseObject(str,Student.class, Feature.AllowSingleQuotes);
        long t2=System.currentTimeMillis();
        System.out.println("ASM:"+flag);
        System.out.println("time:"+(t2-t1));
        System.out.println("age:"+student.getAge());
        System.out.println("name:"+student.getName());
    }

开启asm:
在这里插入图片描述
关闭asm:
在这里插入图片描述

可以看到,是否开启asm并不影响程序的结果,但是会影响程序的执行时间。开启asm下,程序执行时间明显比不开要短。这只是一个非常简单的json字符串的测试结果,在长json字符串的解析中,这一加速效果会更加明显。

总结

Fastjson默认开启asm用来代替Java反射,提高了程序的整体性能。使用Java Bean测试发现开启asm情况下解析速度明显比不开要快。
下一篇我们分析asm在Fastjson中的实现。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
fastjson是一款Java语言编写的高性能JSON处理库,其主要特点是速度快、内存占用低、功能强大、易于使用。下面就简单介绍一下fastjson源码分析。 1. 核心架构 fastjson的核心架构包括JSONReader、JSONWriter、JSONLexer、JSONParser、JSONScanner、JSONSerializer、JSONDeserializer等模块。其中,JSONLexer和JSONScanner是fastjson的词法分析器,JSONParser是解析器,JSONSerializer和JSONDeserializer是序列化和反序列化实现。 2. 序列化和反序列化 fastjson的序列化和反序列化实现主要基于Java的反射机制和ASM字节码生成技术。在序列化时,fastjson会根据Java对象的类型信息来生成相应的序列化代码,并将序列化后的数据输出到JSONWriter中。在反序列化时,fastjson根据JSON数据的类型信息来生成相应的反序列化代码,并将反序列化后的Java对象输出到JSONDeserializer中。 3. 性能优化 fastjson在性能上有很多优化,其中最重要的是使用了预编译技术,将JSON字符串解析为Java对象时,fastjson会对JSON字符串进行预编译,生成一个解析器,以提高解析效率。此外,fastjson还使用了缓存技术,将解析器和序列化器缓存起来,以便重复使用,从而减少了对象的创建和销毁所带来的开销。 4. 使用场景 fastjson广泛应用于互联网领域,如阿里巴巴、淘宝、天猫等都在使用fastjson来处理JSON数据。fastjson还支持多种数据格式的解析和序列化,如XML、CSV等。 以上是fastjson源码分析的简单介绍,相信大家对fastjson有了更深入的了解。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值