中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么理解序列化中的反射

發布時間:2021-10-21 13:43:58 來源:億速云 閱讀:177 作者:iii 欄目:編程語言

本篇內容主要講解“怎么理解序列化中的反射”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么理解序列化中的反射”吧!

前言

序列化大家都不陌生,說白了就是把當前類對象的狀態保存為二進制,然后被用來持久化或者網絡傳輸;常用的RPC框架在數據傳輸前都會進行序列化操作,主流的RPC框架包含了多種序列化方式比如protobuf,fastjson,kryo,hessian,java內置序列化等等,大致可以分為二進制和字符串(json字符串)。

反射

因為需要把當前類對象狀態保存為二進制,所以往往需要獲取所有類屬性,這時候大部分的序列化方式都用到了反射,通過反射獲取所有類屬性獲取方法,然后獲取到屬性值,大致如下:

//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);
}

但是反射往往在性能上被大家所懷疑,所以出現了類似protobuf采用自動生成序列化代碼的方式,fastjson使用ASM代替反射的方式;下面我們先用簡單的測試來對比一下各種方式的性能,看反射是否真的慢;

性能測試

在windows10+jdk8環境下分別對直接,反射,以及ASM調用方法分別進行壓力測試,看起消耗的時間,測試中可以多次執行,取穩定的值;以下測試分別從Person對象通過方法獲取屬性值,如下:

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

直接調用

直接調用也就是我們平時最常用的方式,直接通過對象調用方法名稱獲取屬性值,我們在壓測的時候會分別輪詢兩個方法:

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");
    }

多次測試結果大概在90ms左右,直接調用速度是最快的,但是需要我們手動的寫每個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);
 }

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

反射調用

使用jdk提供的反射機制,獲取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");
    }

經測試時間大概維持在205ms左右,和直接調用還是存在一定差距的,不過jdk每一輪的升級,都在提升性能,比如jdk7中引入的MethodHandle,模擬字節碼層面的調用;

ASM調用

反射是讀取持久堆上存儲的類信息,而ASM是直接處理.class字節碼的,無需加載類,我們這里使用ReflectASM來進行測試;

ReflectASM 是一個非常小的 Java 類庫,通過代碼生成來提供高性能的反射處理,自動為 get/set 字段提供訪問類,訪問類使用字節碼操作而不是 Java 的反射技術,因此非常快。
    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");
    }

經測試時間維持在110ms左右,速度還是很快的,快趕上直接調用了;其中為了獲得最大性能,應使用方法或字段索引而不是名稱;

總結

可以看到雖然反射性能一直在提升,但是相比直接調用和ASM的方式還是有一點差距;但其實如果用在RPC上這點時間在整個網絡傳輸上來說可以說微乎其微;如果對性能極度追求,可以考慮使用直接調用或者ASM的方式;

思考

關于直接調用上面說到protobuf,通過工具生成序列化代碼,但是這種方式每次改動都要手動生成代碼,有點麻煩,是否可以直接利用lombok這種框架做一個擴展,自動生成序列化代碼,其實lombok底層也用到ASM,直接生成字節碼代碼,提供序列化注解

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

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

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

到此,相信大家對“怎么理解序列化中的反射”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

jdk
AI

陕西省| 蒙城县| 肇庆市| 平江县| 新野县| 喜德县| 梅河口市| 清苑县| 彭山县| 淄博市| 顺平县| 自治县| 望奎县| 神木县| 长治市| 信丰县| 贵定县| 巴里| 合川市| 彩票| 马公市| 揭东县| 阳朔县| 呈贡县| 阆中市| 商水县| 安庆市| 徐汇区| 通州区| 南郑县| 东莞市| 寻乌县| 汾阳市| 永寿县| 厦门市| 台东市| 中山市| 西宁市| 济阳县| 黔西县| 滦南县|