当前位置:首页>编程知识库>后端开发知识>为什么不建议使用Java序列化?
为什么不建议使用Java序列化?
阅读 2
2021-06-09
首先,解释一下序列化的概念
把对象转换为字节序列的过程称为对象的序列化。
什么情况下需要用到序列化

把的内存中的对象状态保存到一个文件中或者数据库中时候;

使用套接字在网络上传送对象的时候;

通过RMI传输对象的时候;
总之一句话,只要我们对内存中的对象进行持久化或网络传输, 都需要进行序列化和反序列化。
如果研究过一些常用的RPC通信框架,我们会发现它们极少使用Java自带的序列化,什么原因?

1、无法跨语言

通过Java的原生Serializable接口与ObjectOutputStream实现的序列化,只有java语言自己能通过ObjectInputStream来解码,其他语言,如CCPython等等,都无法对其实现解码。而在我们实际开发生产中,有时不可避免的需要基于不同语言编写的应用程序之间进行通信,这个时候Java自带的序列化就无法搞定了。

2、性能差

我们来对比Java自带的序列化与NIO中的ByteBuffer编码的性能
UserInfo
import java.io.Serializable;
import java.nio.ByteBuffer;
public class UserInfo implements Serializable {
    private static final long serialVersionUID = 1L;

    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public byte[] codeC() {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        byte[] value = this.name.getBytes();
        buffer.putInt(value.length);
        buffer.put(value);
        buffer.putLong(this.id);
        buffer.flip();
        byte[] result = new byte[buffer.remaining()];
        buffer.get(result);
        return result;
    }

    public byte[] codeC(ByteBuffer buffer) {
        buffer.clear();
        byte[] value = this.name.getBytes();
        buffer.putInt(value.length);
        buffer.put(value);
        buffer.putLong(this.id);
        buffer.flip();
        byte[] result = new byte[buffer.remaining()];
        buffer.get(result);
        return result;
    }

}
测试类
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.nio.ByteBuffer;

public class MainTest {

    public static void main(String[] args) throws Exception {
        UserInfo info = new UserInfo();
        info.setId(1L);
        info.setName("Tom");

        int loop = 100_0000;
        ByteArrayOutputStream bout = null;
        ObjectOutputStream out = null;
        long start = System.currentTimeMillis();
        for (int i = 0; i < loop; i  ) {
            bout = new ByteArrayOutputStream();
            out = new ObjectOutputStream(bout);
            out.flush();
            out.close();
            byte[] b = bout.toByteArray();
            bout.close();
        }
        System.out.println("jdk serializable time : "   (System.currentTimeMillis() - start)   " ms");

        System.out.println("------------------------------");

        start = System.currentTimeMillis();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        for (int i = 0; i < loop; i  ) {
            byte[] bytes = info.codeC(buffer);
        }
        System.out.println("ByteBuffer time : "   (System.currentTimeMillis() - start)   " ms");
    }
}
输出结果:

3、序列后的码流太大

java序列化的大小是二进制编码的5倍多!
序列化后的二进制数组越大,占用的存储空间就越多,如果我们是进行网络传输,相对占用的带宽就更多,也会影响系统的性能。
我们来测试一下:
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;

public class MainTest {

    public static void main(String[] args) throws Exception {
        UserInfo info = new UserInfo();
        info.setId(1L);
        info.setName("Tom");
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bout);
        out.writeObject(info);
        out.flush();
        out.close();
        byte[] b = bout.toByteArray();
        System.out.println("jdk serializable: "   b.length);
        bout.close();
        System.out.println("ByteBuffer : "   info.codeC().length);
    }
}
输出结果:
正是由于Java自带的序列化存在这些问题,开源社区涌现出很多优秀的序列化框架。
我们看一下比较主流的序列化框架的特点,可以从以下方面对比:

是否支持跨平台,跨语言是否符合我们系统要求

编码后的码流大小

效率

使用上是否便捷(包括社区维护、API复杂度等)
可以方便我们结合自身业务来选择适合自己的一款,来优化系统的序列化性能。

Protobuf


结构化数据存储格式(xml,json等)

高性能编解码技术

语言和平台无关,扩展性好,支持javaCPython三种语言。

Google开源

Thrift


支持多种语言(CC#、CocoaErlagHaskelljavaOcamiPerlPHPPythonRubySmallTalk

使用了组建大型数据交换及存储工具,对于大型系统中的内部数据传输,相对于Jsonxml在性能上和传输大小上都有明显的优势。

支持通用二进制编码,压缩二进制编码,优化的可选字段压缩编解码等三种方式。

FaceBook开源

Jackson


Jackson所依赖的jar包较少,简单易用并且性能也要相对高些。

对于复杂类型的json转换bean会出现问题,一些集合MapList的转换出现问题。

Jackson对于复杂类型的bean转换Json,转换的json格式不是标准的Json格式

Gson


Gson是目前功能最全的Json解析神器

Gson的应用主要为toJsonfromJson两个转换函数,无依赖,不需要例外额外的jar

类里面只要有getset方法,Gson完全可以将复杂类型的jsonbeanbeanjson的转换,是JSON解析的神器

FastJson


无依赖,不需要例外额外的jar,能够直接跑在JDK上。

FastJson在复杂类型的Bean转换Json上会出现一些问题,可能会出现引用的类型,导致Json转换出错,需要制定引用。

FastJson采用独创的算法,将parse的速度提升到极致,超过所有json库。

频繁爆漏洞,被相当部分公司禁止使用
 
  END
 


 十期推荐
 
  【271期】面试官:Spring MVC的处理流程是怎样的?
  

 
 
  【272期】数据结构:哈希表原理以及面试中的常见考点
  

 
 
  【273期】告诉面试官,我能优化groupBy,而且知道得很深!
  

 
 
  【274期】面试官:怎么保证缓存和数据库一致性
  

 
 
  【275期】面试官:你对MySQL中的索引了解多少?
  

 
 
  【276期】面试官:你分析过@Annotation注解的实现原理吗?
  

 
 
  【277期】面试官:说几种常用的分布式 ID 解决方案
  

 
 
  【278期】面试官:都说 select * 效率低下,你知道什么原因吗?
  

 
 
  【279期】面试官:Java遍历Map集合有哪几种方式?各自效率怎么样?
  

 
 
  【280期】k8s面试问什么?
  

 
 
  

 
? ~
以上数据来源于网络,如有侵权,请联系删除。
评论 (0)