zoukankan      html  css  js  c++  java
  • 序列化和性能(转)

    转载自http://www.360doc.com/content/06/1217/00/15643_296709.shtml

    通常人们认识的序列化类实现java.io.Serializable 像这样:


    import java.io.*;

    public class Pojo implements Serializable {

    private static final long serialVersionUID = L;

    ....

    }

    这种做法被广大程序员广泛应用,然而这种序列化的性能很低。它使用反射机制寻找序列化类的类变量和这些类变量的类型。大家都知道反射是一个非常消耗时间的处理过程。

    我们如何能尽可能的在程序中减轻这种负载呢?有下面三种方法:

    1.使用ObjectStreamField类
    2.使用readObject / writeObject 方法
    3.使用Externalizable 接口

    在上面的方法中,性能最好的是使用Externalizable 接口。为了便于理解,将为不同的方法例举一个例子。

    ObjectStreamField 类为序列化机制提供序列化对象的成员变量以及成员变量的类型。这样做的好是可以节约一部分时间,他不必通过反射机制去查询成员变量以及类型。关键字transient 仍然可以在这种方式下正常使用,不过在创建ObjectStreamField对象添加序列话成员变量的时候,剔除transient 标识的成员变量。

    public class Pojo implements Serializable {

    private static final long serialVersionUID = 1L;

    private String valueA = "SomeTextA";
    private int valueB = 10;
    private float valueC = 100f;
    private double valueD = 100.100d;
    private short valueE = 10;

    // Getters and setters go here.

    private static final ObjectStreamField[] serialPersistFields = {
    new ObjectStreamField("valueA", String.class),
    new ObjectStreamField("valueB", Integer.class),
    new ObjectStreamField("valueC", Float.class),
    new ObjectStreamField("valueD", Double.class),
    new ObjectStreamField("valueE", Short.class)
    };
    }

    就像你所看到的,为了提升性能得必须指明你所序列化的成员变量,当然除了transient 的成员变量。

    第二种方法是使用readObject / writeObject 方法。

    public class Pojo implements Serializable {

    private static final long serialVersionUID = 1L;

    private String valueA = "SomeTextA";
    private int valueB = 10;
    private float valueC = 100f;
    private double valueD = 100.100d;
    private short valueE = 10;

    // Getters and setters go here.

    private void writeObject(ObjectOutputStream oos) throws IOException {
    oos.writeUTF(valueA);
    oos.writeInt(valueB);
    oos.writeFloat(valueC);
    oos.writeDouble(valueD);
    oos.writeShort(valueE);
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    this.valueA = ois.readUTF();
    this.valueB = ois.readInt();
    this.valueC = ois.readFloat();
    this.valueD= ois.readDouble();
    this.valueE= ois.readShort();
    }
    }


    这种方式包括了序列化的序列化和反序列化过程。就像上面所展示的,仍然需要声明Serializable 接口。

    第三种方式使用Externalizable 接口。

    public class Pojo implements Externalizable {

    private static final long serialVersionUID = 1L;

    private String valueA = "SomeTextA";
    private int valueB = 10;
    private float valueC = 100f;
    private double valueD = 100.100d;
    private short valueE = 10;

    // Getters and setters go here.

    public void writeExternal(ObjectOutput out) throws IOException {
    out.writeUTF(valueA);
    out.writeInt(valueB);
    out.writeFloat(valueC);
    out.writeDouble(valueD);
    out.writeShort(valueE);
    }

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    this.valueA = in.readUTF();
    this.valueB = in.readInt();
    this.valueC = in.readFloat();
    this.valueD= in.readDouble();
    this.valueE= in.readShort();
    }
    }


    这种方式与readObject / writeObject 方式不同的是不再需要声明Serializable 接口, readObject / writeObject 方法使用ObjectOutput/ObjectInput 接口作为参数代理输入流输出流实现类,并且serialVersionUID 仍然可以使用。

    下面将测试各种序列化方法所消耗的时间:

    public class Run {

    private static int TIMES = 500000;

    public static void main(String[] args) {
    Pojo sp = new Pojo();

    long start = System.currentTimeMillis();
    for (int i = 0; i<TIMES;i++){
    serialize(sp);
    }
    long duration = System.currentTimeMillis() - start;

    System.out.println("Externalizable: " + duration + "ms.");
    }

    public static void serialize(Pojo o) {
    try {
    ByteArrayOutputStream bout = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(bout);
    out.writeObject(o);
    out.close();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    }


    为了获得正确的测试结果,我们制定以下规则 。首先,不能测试一小部分对象,应该取较大的均值。第二,减小不必要的负载。如果测试程序快速产生大量的对象,会产生大量的垃圾收集。

    在上面的代码中,我们仅序列化了50000个对象。并且没有将序列化的内容写入磁盘或者网络,因为会产生我们不希望的负载。

    还有一点值得注意的是,如果有多个String类型的成员变量,他们之应该是不同的值。如果他们的值相同,那么他们引用的是同一个对象。

    下面是不同jdk版本间的测试结果:

    JDK 1.4.2_12

    Serializable: 9766ms.
    Streamfield: 9656ms.
    Read/Write object: 7781ms.
    Externalizable: 5875ms.


    JDK 1.5.0_19

    Serializable: 9016ms.
    Streamfield: 8859ms.
    Read/Write object: 7141ms.
    Externalizable: 5610ms.

    JDK 1.6.0 (B103)

    Serializable: 7484ms.
    Streamfield: 7312ms.
    Read/Write object: 5610ms.
    Externalizable: 4828ms.

    有趣的是你可以发现使用Externalizable 接口提升了55%的性能。同时也可以看出jdk不同版本的垃圾收集器&Hotpot引擎性能也在提升

  • 相关阅读:
    hitachi2020 C-ThREE
    LOJ#2083. 「NOI2016」优秀的拆分
    BZOJ2754: [SCOI2012]喵星球上的点名
    BZOJ4516: [Sdoi2016]生成魔咒
    AtCoder Beginner Contest 146解题报告
    拉格朗日插值复习笔记
    对于求解单峰函数最值问题的探讨
    BZOJ5509: [Tjoi2019]甲苯先生的滚榜
    面试技巧
    性能案例分析 | MAT分析内存泄露
  • 原文地址:https://www.cnblogs.com/xirtam/p/3578853.html
Copyright © 2011-2022 走看看