真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

怎么理解序列化中的反射

本篇內(nèi)容主要講解“怎么理解序列化中的反射”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“怎么理解序列化中的反射”吧!

網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、小程序定制開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了高坪免費(fèi)建站歡迎大家使用!

前言

序列化大家都不陌生,說(shuō)白了就是把當(dāng)前類對(duì)象的狀態(tài)保存為二進(jìn)制,然后被用來(lái)持久化或者網(wǎng)絡(luò)傳輸;常用的RPC框架在數(shù)據(jù)傳輸前都會(huì)進(jìn)行序列化操作,主流的RPC框架包含了多種序列化方式比如protobuf,fastjson,kryo,hessian,java內(nèi)置序列化等等,大致可以分為二進(jìn)制和字符串(json字符串)。

反射

因?yàn)樾枰旬?dāng)前類對(duì)象狀態(tài)保存為二進(jìn)制,所以往往需要獲取所有類屬性,這時(shí)候大部分的序列化方式都用到了反射,通過(guò)反射獲取所有類屬性獲取方法,然后獲取到屬性值,大致如下:

//1.方法Method[] methods = obj.getClass().getDeclaredMethods();for(Method method : methods) {
    method.invoke(obj);
}//2.字段Field fields[] = obj.getClass().getDeclaredFields();for (Field field : fields) {
    field.get(obj);
}

但是反射往往在性能上被大家所懷疑,所以出現(xiàn)了類似protobuf采用自動(dòng)生成序列化代碼的方式,fastjson使用ASM代替反射的方式;下面我們先用簡(jiǎn)單的測(cè)試來(lái)對(duì)比一下各種方式的性能,看反射是否真的慢;

性能測(cè)試

在windows10+jdk8環(huán)境下分別對(duì)直接,反射,以及ASM調(diào)用方法分別進(jìn)行壓力測(cè)試,看起消耗的時(shí)間,測(cè)試中可以多次執(zhí)行,取穩(wěn)定的值;以下測(cè)試分別從Person對(duì)象通過(guò)方法獲取屬性值,如下:

public class Person {private String id;private String name;    public String getId() {return id;
    }public String getName() {return name;
    }
}

直接調(diào)用

直接調(diào)用也就是我們平時(shí)最常用的方式,直接通過(guò)對(duì)象調(diào)用方法名稱獲取屬性值,我們?cè)趬簻y(cè)的時(shí)候會(huì)分別輪詢兩個(gè)方法:

public static void test() {
    Person person = new Person("10001", "zhaohui");long startTime = System.currentTimeMillis();for (int i = 0; i < 1_0000_0000; i++) {if (i % 2 == 0) {
            person.getId();
        } else {
            person.getName();
        }
    }long endTime = System.currentTimeMillis();
    System.out.println("Manual time:" + (endTime - startTime) + "ms");
    }

多次測(cè)試結(jié)果大概在90ms左右,直接調(diào)用速度是最快的,但是需要我們手動(dòng)的寫每個(gè)bean的序列化代碼,或者像protobuf一樣使用工具給我們生成所有的序列化代碼,比如生成Person的序列化代碼:

 public void writeTo(com.google.protobuf.CodedOutputStream output)throws java.io.IOException {getSerializedSize();if (((bitField0_ & 0x00000001) == 0x00000001)) {      output.writeInt32(1, id_);
    }if (((bitField0_ & 0x00000002) == 0x00000002)) {      output.writeBytes(2, getNameBytes());
    }getUnknownFields().writeTo(output);
 }

可以看到每個(gè)生成的bean都自動(dòng)生成了序列化代碼,并且所有的bean都繼承于統(tǒng)一的抽象類,這樣提供一整套規(guī)范;有個(gè)缺點(diǎn)就是每次修改需要手動(dòng)改proto文件,然后重新生成代碼;

反射調(diào)用

使用jdk提供的反射機(jī)制,獲取Methods,然后獲取屬性值,具體代碼如下:

    public static void test() throws Exception {long startTime = System.currentTimeMillis();
        Person person = new Person("10001", "zhaohui");
        Method[] ms = Person.class.getDeclaredMethods();for (int i = 0; i < 1_0000_0000; i++) {
            ms[i & ms.length - 1].invoke(person);
        }long endTime = System.currentTimeMillis();
        System.out.println("Reflex time:" + (endTime - startTime) + "ms");
    }

經(jīng)測(cè)試時(shí)間大概維持在205ms左右,和直接調(diào)用還是存在一定差距的,不過(guò)jdk每一輪的升級(jí),都在提升性能,比如jdk7中引入的MethodHandle,模擬字節(jié)碼層面的調(diào)用;

ASM調(diào)用

反射是讀取持久堆上存儲(chǔ)的類信息,而ASM是直接處理.class字節(jié)碼的,無(wú)需加載類,我們這里使用ReflectASM來(lái)進(jìn)行測(cè)試;

ReflectASM 是一個(gè)非常小的 Java 類庫(kù),通過(guò)代碼生成來(lái)提供高性能的反射處理,自動(dòng)為 get/set 字段提供訪問(wèn)類,訪問(wèn)類使用字節(jié)碼操作而不是 Java 的反射技術(shù),因此非???。
    public static void test() {
        Person person = new Person("10001", "zhaohui");long startTime = System.currentTimeMillis();

        MethodAccess methodAccess = MethodAccess.get(Person.class);
        String[] mns = methodAccess.getMethodNames();int len = mns.length;int indexs[] = new int[len];for (int i = 0; i < len; i++) {
            indexs[i] = methodAccess.getIndex(mns[i]);
        }for (int i = 0; i < 1_0000_0000; i++) {
            methodAccess.invoke(person, indexs[i & len - 1]);
        }long endTime = System.currentTimeMillis();
        System.out.println("ASM time:" + (endTime - startTime) + "ms");
    }

經(jīng)測(cè)試時(shí)間維持在110ms左右,速度還是很快的,快趕上直接調(diào)用了;其中為了獲得最大性能,應(yīng)使用方法或字段索引而不是名稱;

總結(jié)

可以看到雖然反射性能一直在提升,但是相比直接調(diào)用和ASM的方式還是有一點(diǎn)差距;但其實(shí)如果用在RPC上這點(diǎn)時(shí)間在整個(gè)網(wǎng)絡(luò)傳輸上來(lái)說(shuō)可以說(shuō)微乎其微;如果對(duì)性能極度追求,可以考慮使用直接調(diào)用或者ASM的方式;

思考

關(guān)于直接調(diào)用上面說(shuō)到protobuf,通過(guò)工具生成序列化代碼,但是這種方式每次改動(dòng)都要手動(dòng)生成代碼,有點(diǎn)麻煩,是否可以直接利用lombok這種框架做一個(gè)擴(kuò)展,自動(dòng)生成序列化代碼,其實(shí)lombok底層也用到ASM,直接生成字節(jié)碼代碼,提供序列化注解

@Target(ElementType.TYPE)@Retention(RetentionPolicy.SOURCE)
public @interface Serialize {
}

然后可以直接把注解應(yīng)用到bean中,直接幫助我們生成序列化代碼,就像@Getter/@Setter一樣;相當(dāng)于直接調(diào)用和ASM方式的一種整合;類似如下代碼:

@Serializepublic class Person {private String id;private String name;    //自動(dòng)生成public byte[] serialize(){
        ByteBuffer bb = ByteBuffer.allocate(100);
        bb.put(id.getBytes());
        bb.put(name.getBytes());return bb.array();
    }
}

到此,相信大家對(duì)“怎么理解序列化中的反射”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!


本文名稱:怎么理解序列化中的反射
網(wǎng)站URL:http://weahome.cn/article/pcgics.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部