我有一个客户端和服务器通过专有消息系统上的 Spring 远程处理(使用 Java 序列化)进行通信。我的服务器返回大对象,因此我的 Spring 远程处理实现将序列化对象字节数组拆分为 block ,并发送多条消息。客户端等待给定请求的所有响应消息,并最终调用下面的方法将字节数组反序列化为结果对象。
protected Object deserialize(List<byte[]> blocks) {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream(blocks.size() * blockSize);
for (byte[] b : blocks) {
os.write(b, 0, b.length);
}
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
ObjectInputStream objInputStream = new ObjectInputStream(is);
return objInputStream.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
这非常有效。然而,它的内存力很重。假设内存中的对象与其在内存中的序列化字节数组的大小大致相同,我最终得到的大小大约是内存中对象大小的 3 倍:
List<byte[]>
包含 blockByteArrayOutputStream
包含连接的字节数组(可能还有另一个,因为ByteArrayOutputStream.toByteArray()
复制数组)。- 生成的对象
一旦此方法返回,所有数组都可以被 GC 回收,但在此方法调用期间,内存使用量会出现大幅上升。
所以,对于我的问题:有没有一种方法可以创建一个阻塞字节输入流,我可以在收到字节数组时将其附加到其中? ObjectOutputStream 将(在单独的线程中)读取可用字节,然后阻塞直到写入更多字节,并继续直到对象完全反序列化。这样,我就不必在内存中保存完整的串联字节数组。标准流实现似乎都不适合,我看不出如何使用 NIO 来做到这一点,而且如果有足够的流实现,我宁愿不编写自己的流实现。
非常感谢, 伊恩
最佳答案
实现您自己的输入流以减少数组开销
protected Object deserialize(final List<byte[]> blocks) {
try {
ObjectInputStream objInputStream = new ObjectInputStream(InputStream(){
Iterator<byte[]> it=blocks.iterator();
byte[] curr;
int ind;
public int read(){
if(curr==null||curr.length==ind){
if(!it.hasNext())return -1;//or use a blocking queue and pop
curr=it.next();
ind=0;
}
return curr[ind++];
}
});
return objInputStream.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
当然,为了提高效率,您还应该重写read(byte[],int,int)
,但如果速度有点慢,这会起作用
或者您可以使用 PipedInputStream
和 PipedOutputStream
组合你真正想要的。输入流将阻塞,直到有东西要读取
关于java - 如何反序列化字节数组 block 而不放入一个大数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9796412/