目次
まえがき
パフォーマンスの比較
事前準備
テスト クラスの書き込み:
ホームページ Java &#&チュートリアル Java で一般的に使用されるシリアル化メソッドは何ですか? Kryo、Protostuff、Hessian を例として、その実装原理を説明します。

Java で一般的に使用されるシリアル化メソッドは何ですか? Kryo、Protostuff、Hessian を例として、その実装原理を説明します。

May 07, 2023 pm 09:01 PM
java kryo protostuff

    まえがき

    少し前に、RPC フレームワークを作成するときに、Kryo、Hessian、Protostuff という 3 つのシリアル化メソッドを使用しました。ただし、当時は機能を実装したいという気持ちが強かったため、これら 3 つのシリアル化メソッドの使用方法を簡単に説明しただけで、それぞれの特徴やメリット、デメリットについては深く掘り下げませんでした。 RPC フレームワークの作成が完了したことがわかったので、落ち着いて 3 つの方法を比較して要約する時間があります。

    Kryo、Hessain、および Protostuff はすべて、サードパーティのオープン ソースのシリアル化/逆シリアル化フレームワークです。それぞれの特性を理解するには、まずシリアル化/逆シリアル化とは何かを知る必要があります:

    シリアル化: は、オブジェクトをバイト シーケンスに変換するプロセスです。

    逆シリアル化: は、バイト シーケンスをオブジェクトに変換するプロセスです。

    serialization シリアル化: オブジェクトを送信に便利な形式に変換します。一般的なシリアル化形式: バイナリ形式、バイト配列、json 文字列、xml 文字列。

    デシリアライゼーション デシリアライゼーション: シリアル化されたデータをオブジェクトに復元するプロセス

    シリアル化に関連する概念がよくわからない場合は、Meituan を参照してください。技術チームのシリアル化と逆シリアル化

    パフォーマンスの比較

    事前準備

    • 最初に新しい Maven プロジェクトを作成します

    • 次に、依存関係をインポートします

    • ##
      <dependency>
          <groupId>org.junit.jupiter</groupId>
          <artifactId>junit-jupiter-api</artifactId>
          <version>5.8.2</version>
          <scope>test</scope>
      </dependency>
      <!-- 代码简化 -->
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.20</version>
      </dependency>
      <!--kryo-->
      <dependency>
          <groupId>com.esotericsoftware</groupId>
          <artifactId>kryo-shaded</artifactId>
          <version>4.0.2</version>
      </dependency>
      <dependency>
          <groupId>commons-codec</groupId>
          <artifactId>commons-codec</artifactId>
          <version>1.10</version>
      </dependency>
      <!--protostuff-->
      <dependency>
          <groupId>io.protostuff</groupId>
          <artifactId>protostuff-core</artifactId>
          <version>1.7.2</version>
      </dependency>
      <dependency>
          <groupId>io.protostuff</groupId>
          <artifactId>protostuff-runtime</artifactId>
          <version>1.7.2</version>
      </dependency>
      <!--hessian2-->
      <dependency>
          <groupId>com.caucho</groupId>
          <artifactId>hessian</artifactId>
          <version>4.0.62</version>
      </dependency>
      ログイン後にコピー
    #Tool class:

    ##kryo

    package cuit.pymjl.utils;
    import com.esotericsoftware.kryo.Kryo;
    import com.esotericsoftware.kryo.io.Input;
    import com.esotericsoftware.kryo.io.Output;
    import org.apache.commons.codec.binary.Base64;
    import org.objenesis.strategy.StdInstantiatorStrategy;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.UnsupportedEncodingException;
    /**
     * @author Pymjl
     * @version 1.0
     * @date 2022/4/18 20:07
     **/
    @SuppressWarnings("all")
    public class KryoUtils {
        private static final String DEFAULT_ENCODING = "UTF-8";
    
        //每个线程的 Kryo 实例
        private static final ThreadLocal<Kryo> KRYO_LOCAL = new ThreadLocal<Kryo>() {
            @Override
            protected Kryo initialValue() {
                Kryo kryo = new Kryo();
    
                /**
                 * 不要轻易改变这里的配置!更改之后,序列化的格式就会发生变化,
                 * 上线的同时就必须清除 Redis 里的所有缓存,
                 * 否则那些缓存再回来反序列化的时候,就会报错
                 */
                //支持对象循环引用(否则会栈溢出)
                kryo.setReferences(true); //默认值就是 true,添加此行的目的是为了提醒维护者,不要改变这个配置
    
                //不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;而且业务系统中大量的 Class 也难以一一注册)
                kryo.setRegistrationRequired(false); //默认值就是 false,添加此行的目的是为了提醒维护者,不要改变这个配置
    
                //Fix the NPE bug when deserializing Collections.
                ((Kryo.DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy())
                        .setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());
    
                return kryo;
            }
        };
        /**
         * 获得当前线程的 Kryo 实例
         *
         * @return 当前线程的 Kryo 实例
         */
        public static Kryo getInstance() {
            return KRYO_LOCAL.get();
        }
    
        //-----------------------------------------------
        //          序列化/反序列化对象,及类型信息
        //          序列化的结果里,包含类型的信息
        //          反序列化时不再需要提供类型
        //-----------------------------------------------
    
        /**
         * 将对象【及类型】序列化为字节数组
         *
         * @param obj 任意对象
         * @param <T> 对象的类型
         * @return 序列化后的字节数组
         */
        public static <T> byte[] writeToByteArray(T obj) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            Output output = new Output(byteArrayOutputStream);
    
            Kryo kryo = getInstance();
            kryo.writeClassAndObject(output, obj);
            output.flush();
    
            return byteArrayOutputStream.toByteArray();
        }
        /**
         * 将对象【及类型】序列化为 String
         * 利用了 Base64 编码
         *
         * @param obj 任意对象
         * @param <T> 对象的类型
         * @return 序列化后的字符串
         */
        public static <T> String writeToString(T obj) {
            try {
                return new String(Base64.encodeBase64(writeToByteArray(obj)), DEFAULT_ENCODING);
            } catch (UnsupportedEncodingException e) {
                throw new IllegalStateException(e);
            }
        }
        /**
         * 将字节数组反序列化为原对象
         *
         * @param byteArray writeToByteArray 方法序列化后的字节数组
         * @param <T>       原对象的类型
         * @return 原对象
         */
        @SuppressWarnings("unchecked")
        public static <T> T readFromByteArray(byte[] byteArray) {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
            Input input = new Input(byteArrayInputStream);
    
            Kryo kryo = getInstance();
            return (T) kryo.readClassAndObject(input);
        }
        /**
         * 将 String 反序列化为原对象
         * 利用了 Base64 编码
         *
         * @param str writeToString 方法序列化后的字符串
         * @param <T> 原对象的类型
         * @return 原对象
         */
        public static <T> T readFromString(String str) {
            try {
                return readFromByteArray(Base64.decodeBase64(str.getBytes(DEFAULT_ENCODING)));
            } catch (UnsupportedEncodingException e) {
                throw new IllegalStateException(e);
            }
        }
        //-----------------------------------------------
        //          只序列化/反序列化对象
        //          序列化的结果里,不包含类型的信息
        //-----------------------------------------------
    
        /**
         * 将对象序列化为字节数组
         *
         * @param obj 任意对象
         * @param <T> 对象的类型
         * @return 序列化后的字节数组
         */
        public static <T> byte[] writeObjectToByteArray(T obj) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            Output output = new Output(byteArrayOutputStream);
            Kryo kryo = getInstance();
            kryo.writeObject(output, obj);
            output.flush();
            return byteArrayOutputStream.toByteArray();
        }
    
        /**
         * 将对象序列化为 String
         * 利用了 Base64 编码
         *
         * @param obj 任意对象
         * @param <T> 对象的类型
         * @return 序列化后的字符串
         */
        public static <T> String writeObjectToString(T obj) {
            try {
                return new String(Base64.encodeBase64(writeObjectToByteArray(obj)), DEFAULT_ENCODING);
            } catch (UnsupportedEncodingException e) {
                throw new IllegalStateException(e);
            }
        }
        /**
         * 将字节数组反序列化为原对象
         *
         * @param byteArray writeToByteArray 方法序列化后的字节数组
         * @param clazz     原对象的 Class
         * @param <T>       原对象的类型
         * @return 原对象
         */
        @SuppressWarnings("unchecked")
        public static <T> T readObjectFromByteArray(byte[] byteArray, Class<T> clazz) {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
            Input input = new Input(byteArrayInputStream);
    
            Kryo kryo = getInstance();
            return kryo.readObject(input, clazz);
        }
        /**
         * 将 String 反序列化为原对象
         * 利用了 Base64 编码
         *
         * @param str   writeToString 方法序列化后的字符串
         * @param clazz 原对象的 Class
         * @param <T>   原对象的类型
         * @return 原对象
         */
        public static <T> T readObjectFromString(String str, Class<T> clazz) {
            try {
                return readObjectFromByteArray(Base64.decodeBase64(str.getBytes(DEFAULT_ENCODING)), clazz);
            } catch (UnsupportedEncodingException e) {
                throw new IllegalStateException(e);
            }
        }
    }
    ログイン後にコピー
    Hessian

    package cuit.pymjl.utils;
    import com.caucho.hessian.io.Hessian2Input;
    import com.caucho.hessian.io.Hessian2Output;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    
    /**
     * @author Pymjl
     * @version 1.0
     * @date 2022/7/2 12:39
     **/
    public class HessianUtils {
        /**
         * 序列化
         *
         * @param obj obj
         * @return {@code byte[]}
         */
        public static byte[] serialize(Object obj) {
            Hessian2Output ho = null;
            ByteArrayOutputStream baos = null;
            try {
                baos = new ByteArrayOutputStream();
                ho = new Hessian2Output(baos);
                ho.writeObject(obj);
                ho.flush();
                return baos.toByteArray();
            } catch (Exception ex) {
                ex.printStackTrace();
                throw new RuntimeException("serialize failed");
            } finally {
                if (null != ho) {
                    try {
                        ho.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (null != baos) {
                    try {
                        baos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        /**
         * 反序列化
         *
         * @param bytes 字节
         * @param clazz clazz
         * @return {@code T}
         */
        public static  <T> T deserialize(byte[] bytes, Class<T> clazz) {
            Hessian2Input hi = null;
            ByteArrayInputStream bais = null;
            try {
                bais = new ByteArrayInputStream(bytes);
                hi = new Hessian2Input(bais);
                Object o = hi.readObject();
                return clazz.cast(o);
            } catch (Exception ex) {
                throw new RuntimeException("deserialize failed");
            } finally {
                if (null != hi) {
                    try {
                        hi.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (null != bais) {
                    try {
                        bais.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    ログイン後にコピー
    Protostuff

    package cuit.pymjl.utils;
    import io.protostuff.LinkedBuffer;
    import io.protostuff.ProtostuffIOUtil;
    import io.protostuff.Schema;
    import io.protostuff.runtime.RuntimeSchema;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    /**
     * @author Pymjl
     * @version 1.0
     * @date 2022/6/28 21:00
     **/
    public class ProtostuffUtils {
        /**
         * 避免每次序列化都重新申请Buffer空间
         * 这个字段表示,申请一个内存空间用户缓存,LinkedBuffer.DEFAULT_BUFFER_SIZE表示申请了默认大小的空间512个字节,
         * 我们也可以使用MIN_BUFFER_SIZE,表示256个字节。
         */
        private static final LinkedBuffer BUFFER = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        /**
         * 缓存Schema
         * 这个字段表示缓存的Schema。那这个Schema是什么呢?就是一个组织结构,就好比是数据库中的表、视图等等这样的组织机构,
         * 在这里表示的就是序列化对象的结构。
         */
        private static final Map<Class<?>, Schema<?>> SCHEMA_CACHE = new ConcurrentHashMap<>();
    
        /**
         * 序列化方法,把指定对象序列化成字节数组
         *
         * @param obj 对象
         * @return byte[]
         */
        @SuppressWarnings("unchecked")
        public static <T> byte[] serialize(T obj) {
            Class<T> clazz = (Class<T>) obj.getClass();
            Schema<T> schema = getSchema(clazz);
            byte[] data;
            try {
                data = ProtostuffIOUtil.toByteArray(obj, schema, BUFFER);
            } finally {
                BUFFER.clear();
            }
            return data;
        }
    
        /**
         * 反序列化方法,将字节数组反序列化成指定Class类型
         *
         * @param data  字节数组
         * @param clazz 字节码
         * @return
         */
        public static <T> T deserialize(byte[] data, Class<T> clazz) {
            Schema<T> schema = getSchema(clazz);
            T obj = schema.newMessage();
            ProtostuffIOUtil.mergeFrom(data, obj, schema);
            return obj;
        }
        @SuppressWarnings("unchecked")
        private static <T> Schema<T> getSchema(Class<T> clazz) {
            Schema<T> schema = (Schema<T>) SCHEMA_CACHE.get(clazz);
            if (schema == null) {
                schema = RuntimeSchema.getSchema(clazz);
                if (schema == null) {
                    SCHEMA_CACHE.put(clazz, schema);
                }
            }
            return schema;
        }
    }
    ログイン後にコピー
    テスト用のエンティティ クラスを作成します:

    package cuit.pymjl.entity;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import java.io.Serial;
    import java.io.Serializable;
    /**
     * @author Pymjl
     * @version 1.0
     * @date 2022/7/2 12:32
     **/
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Student implements Serializable {
        @Serial
        private static final long serialVersionUID = -91809837793898L;
    
        private String name;
        private String password;
        private int age;
        private String address;
        private String phone;
    }
    ログイン後にコピー
    空間の比較シリアル化後にバイトが占有

    テスト クラスの書き込み:

    public class MainTest {
        @Test
        void testLength() {
            Student student = new Student("pymjl", "123456", 18, "北京", "123456789");
            int kryoLength = KryoUtils.writeObjectToByteArray(student).length;
            int hessianLength = HessianUtils.serialize(student).length;
            int protostuffLength = ProtostuffUtils.serialize(student).length;
            System.out.println("kryoLength: " + kryoLength);
            System.out.println("hessianLength: " + hessianLength);
            System.out.println("protostuffLength: " + protostuffLength);
        }
    }
    ログイン後にコピー
    スクリーンショットの実行:

    図からわかるように、ヘッセ行列のシリアル化後にバイトが占めるスペースは、他の 2 つの方法よりも大幅に大きくなります。Java で一般的に使用されるシリアル化メソッドは何ですか? Kryo、Protostuff、Hessian を例として、その実装原理を説明します。

    その他の比較

    ヘッセ行列は固定長を使用しますkryo は、この基本データ型がシリアル化後に可能な限り小さくなるようにするために、可変長の int と Long を使用しますが、実際のアプリケーションでは、大きなデータが頻繁に表示されることはありません。
    • Kryo がシリアル化するときは、完全なクラス名を渡すか、 register() を使用して事前にクラスを Kryo に登録する必要があります。そのクラスは int 型 ID に関連付けられており、シーケンスにはこの ID のみが格納されるため、シーケンスのボリュームは小さくなりますが、ヘッシアンではすべてのクラス フィールド情報がシリアル化されたバイト配列に配置され、他の関与なしに、そのバイト配列が逆シリアル化に直接使用されます。 -処理が遅くなります
    • #Kryo は Serializable インターフェイスを実装する必要はありませんが、Hessian は Kryo データ クラスのフィールド増加を実装する必要があります

    • 、マイナス、シリアル化と逆シリアル化は互換性がありませんが、ヘシアンは互換性があります。Protostuff は最後に新しいフィールドを追加することによってのみ互換性があります。

    • #Kryo とヘシアンの使用には、データ クラスには引数のないコンストラクターが必要です。

    • #Hessian は、複雑なオブジェクトのすべてのプロパティをシリアル化のために Map に格納します。そのため、親クラスとサブクラスに同名のメンバ変数が存在する場合、ヘッセ行列化の際、サブクラスが先にシリアル化され、その後親クラスがシリアル化されるため、逆シリアル化の結果、同名のメンバ変数が発生します。

    • Kryo はスレッド セーフではありません。スレッド セーフを確保するには、ThreadLocal を使用するか、Kryo スレッド プールを作成する必要があります。 Protostuff はスレッドセーフです
    • Protostuff と Kryo シリアル化の形式は似ています。どちらもフィールド タイプを記録するためにマークを使用するため、シリアル化される量は比較的小さくなります。
    • 概要

    メリットデメリットクロス言語サポートは複雑です低速静的コンパイルが必要デフォルトのコンストラクターのないクラスはサポートされません。ユーザーは逆シリアル化中にシリアル化されたオブジェクトを自分で初期化する必要があります、オブジェクトの割り当てのみを担当します#遅くてスペースを消費します
    Kryo 高速、シリアル化後のサイズは小さい
    Hessian デフォルトのクロス言語サポート
    Protostuff 高速、protobufに基づく
    Protostuff-Runtime 静的コンパイルは必要ありませんが、シリアル化の前にスキーマを渡す必要があります
    Java 使いやすく、すべてのクラスをシリアル化できます

    以上がJava で一般的に使用されるシリアル化メソッドは何ですか? Kryo、Protostuff、Hessian を例として、その実装原理を説明します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    このウェブサイトの声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

    ホットAIツール

    Undresser.AI Undress

    Undresser.AI Undress

    リアルなヌード写真を作成する AI 搭載アプリ

    AI Clothes Remover

    AI Clothes Remover

    写真から衣服を削除するオンライン AI ツール。

    Undress AI Tool

    Undress AI Tool

    脱衣画像を無料で

    Clothoff.io

    Clothoff.io

    AI衣類リムーバー

    AI Hentai Generator

    AI Hentai Generator

    AIヘンタイを無料で生成します。

    ホットツール

    メモ帳++7.3.1

    メモ帳++7.3.1

    使いやすく無料のコードエディター

    SublimeText3 中国語版

    SublimeText3 中国語版

    中国語版、とても使いやすい

    ゼンドスタジオ 13.0.1

    ゼンドスタジオ 13.0.1

    強力な PHP 統合開発環境

    ドリームウィーバー CS6

    ドリームウィーバー CS6

    ビジュアル Web 開発ツール

    SublimeText3 Mac版

    SublimeText3 Mac版

    神レベルのコード編集ソフト(SublimeText3)

    Javaの完全数 Javaの完全数 Aug 30, 2024 pm 04:28 PM

    Java における完全数のガイド。ここでは、定義、Java で完全数を確認する方法、コード実装の例について説明します。

    Java の乱数ジェネレーター Java の乱数ジェネレーター Aug 30, 2024 pm 04:27 PM

    Java の乱数ジェネレーターのガイド。ここでは、Java の関数について例を挙げて説明し、2 つの異なるジェネレーターについて例を挙げて説明します。

    ジャワのウェカ ジャワのウェカ Aug 30, 2024 pm 04:28 PM

    Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

    Javaのスミス番号 Javaのスミス番号 Aug 30, 2024 pm 04:28 PM

    Java のスミス番号のガイド。ここでは定義、Java でスミス番号を確認する方法について説明します。コード実装の例。

    Java Springのインタビューの質問 Java Springのインタビューの質問 Aug 30, 2024 pm 04:29 PM

    この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

    Java 8 Stream Foreachから休憩または戻ってきますか? Java 8 Stream Foreachから休憩または戻ってきますか? Feb 07, 2025 pm 12:09 PM

    Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

    Java での日付までのタイムスタンプ Java での日付までのタイムスタンプ Aug 30, 2024 pm 04:28 PM

    Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

    未来を創る: まったくの初心者のための Java プログラミング 未来を創る: まったくの初心者のための Java プログラミング Oct 13, 2024 pm 01:32 PM

    Java は、初心者と経験豊富な開発者の両方が学習できる人気のあるプログラミング言語です。このチュートリアルは基本的な概念から始まり、高度なトピックに進みます。 Java Development Kit をインストールしたら、簡単な「Hello, World!」プログラムを作成してプログラミングを練習できます。コードを理解したら、コマンド プロンプトを使用してプログラムをコンパイルして実行すると、コンソールに「Hello, World!」と出力されます。 Java の学習はプログラミングの旅の始まりであり、習熟が深まるにつれて、より複雑なアプリケーションを作成できるようになります。

    See all articles