前言
基于Java提供的对象输入、输出流ObjectInputStream和ObjectOutputStream,可以直接把Java对象作为可存储的字节数组写入文件,也可以传输到网络上,基于JDK默认的序列化机制可以避免操作底层的字节数组,从而提升开发效率。
Java序列化的缺点
Java序列化从JDK1.1版本就已经提供,它不需要添加额外的类库,只需实现java.io.Serializable并生成序列ID即可,因此,它从诞生之初就得到了广泛的应用。
但是在远程服务调用(RPC)时,很少直接使用Java系列化进行消息的编解码和传输,原因主要归为如下几点。
1、无法跨语言
对于跨进程的服务调用,服务提供者可能会使用 C++或者其他语言开发,当我们需要和异构语言进程交互时 Java 序列化就难以胜任。由于 Java 序列化技术是 Java 语言内部的私有协议,其他语言并不支持,对于用户来说它完全是黑盒。对于 Java 序列化后的字节数组,别的语言无法进行反序列化,这就严重阻碍了它的应用。
2、序列化后的码流太大
测试案例,对比Java序列化后的字节数组大小和使用ByteBuffer进行编码后的大小。
import java.io.Serializable;
import java.nio.ByteBuffer;
public class UserInfo implements Serializable {
private String userName;
private int age;
public void setUserName(String userName) {
this.userName = userName;
}
public void setAge(int age) {
this.age = age;
}
public byte[] codeC() {
ByteBuffer buffer = ByteBuffer.allocate(1024);
byte[] value = this.userName.getBytes();
buffer.putInt(value.length);
buffer.put(value);
buffer.putInt(age);
buffer.flip();
byte[] result = new byte[buffer.remaining()];
buffer.get(result);
return result;
}
}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class TestUserInfo {
public static void main(String[] args) throws IOException {
UserInfo userInfo = new UserInfo();
userInfo.setUserName("test netty Serializable");
userInfo.setAge(18);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(userInfo);
oos.flush();
oos.close();
byte[] b = bos.toByteArray();
System.out.println("jdk Serializable length : " + b.length);
bos.close();
System.out.println("byte Serializable length : " + userInfo.codeC().length);
}
}
差距显而易见,编码后的字节数组越大,存储的时候就越占空间,存储的硬件成本就越高,并且在网络传输时更占宽带,导致系统的吞吐量降低。
3、序列化性能太低
稍作修改
public byte[] codeC(ByteBuffer buffer) {
buffer.clear();
byte[] value = this.userName.getBytes();
buffer.putInt(value.length);
buffer.put(value);
buffer.putInt(age);
buffer.flip();
value = null;
byte[] result = new byte[buffer.remaining()];
buffer.get(result);
return result;
}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.nio.ByteBuffer;
public class TestUserInfo {
public static void main(String[] args) throws IOException {
UserInfo userInfo = new UserInfo();
userInfo.setUserName("test netty Serializable");
userInfo.setAge(18);
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
int loop = 1000000;
long startTime = System.currentTimeMillis();
for (int i = 0; i < loop; i++) {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(userInfo);
oos.flush();
oos.close();
byte[] b = bos.toByteArray();
bos.close();
}
long endTime = System.currentTimeMillis();
System.out.println("jdk Serializable time : " + (endTime - startTime));
ByteBuffer buffer = ByteBuffer.allocate(1024);
startTime = System.currentTimeMillis();
for (int i = 0; i < loop; i++) {
byte[] b = userInfo.codeC(buffer);
}
endTime = System.currentTimeMillis();
System.out.println("byte Serializable time : " + (endTime - startTime));
}
}
总结
从结果可以看出,无论是序列化后的码流大小,还是序列化的性能,JDK默认的序列化机制表现的都很差,因此,我们通常不会选择Java序列化作为远程跨节点调用的编解码框架。