使用带有套接字的 Java 对象流的性能问题
Posted
技术标签:
【中文标题】使用带有套接字的 Java 对象流的性能问题【英文标题】:Performance issue using Javas Object streams with Sockets 【发布时间】:2011-01-16 02:26:08 【问题描述】:我正在尝试使用 Java 中的套接字和对象流进行本地 IPC,但是我发现性能很差。
我正在测试通过 ObjectOutputStream 发送对象到通过 Socket 通过 ObjectInputStream 接收回复的 ping 时间。
这是请求者:
public SocketTest()
int iterations = 100;
try
Socket socket = new Socket("localhost", 1212);
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
double start = System.currentTimeMillis();
for (int i = 0; i < iterations; ++i)
Request request = new Request();
objectOutputStream.writeObject(request);
Response response = (Response)objectInputStream.readObject();
double finish = System.currentTimeMillis();
System.out.println("Per ping: " + (finish - start) / iterations );
catch (Exception e)
e.printStackTrace();
这是响应者:
public ServerSocketTest()
try
ServerSocket serverSocket = new ServerSocket(1212);
Socket socket = serverSocket.accept();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
Request request = (Request)objectInputStream.readObject();
while (request != null)
Response response = new Response();
objectOutputStream.writeObject(response);
request = (Request)objectInputStream.readObject();
catch (Exception e)
e.printStackTrace();
我得到的结果是:
每次 ping:80.35
80 毫秒对于本地流量来说太慢了。
Request 和 Response 类非常小,它们的序列化速度很快。
我曾尝试天真地添加:
socket.setKeepAlive(true);
socket.setTcpNoDelay(true);
影响不大。
执行 ping 本地主机:
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=0 ttl=64 time=0.035 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=64 time=0.037 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=2 ttl=64 time=0.049 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=3 ttl=64 time=0.039 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=4 ttl=64 time=0.056 ms
也很快。
Java 版本 1.6.0_05l 在 RedHat 2.4 上运行
【问题讨论】:
【参考方案1】:您是否尝试在 BufferedInputStream/BufferedOutputStream 中同时嵌入请求和响应?它应该会大大提高性能。
【讨论】:
我在两端都改成了: ObjectOutputStream objectOutputStream = new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream())); ObjectInputStream objectInputStream = new ObjectInputStream(new BufferedInputStream(socket.getInputStream()));它得到了很大的改善!每个 ping:0.1354 我仍然认为它会比这更快。我还能做些什么来改善这一点吗?【参考方案2】:所以在构造 BufferedInputStream 之前构造 BufferedOutputStream 并刷新它。避免挂起。
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4788782
它是按照文档说的
如果您修改测试用例,使得 AServer 和 AClient 构造 之前的 ObjectOutputStream ObjectInputStream,测试没有 堵塞。这是预期的 给出以下行为 文档:
ObjectOutputStream 构造函数: 创建一个写入指定的 ObjectOutputStream 输出流。这个构造函数写 序列化流标头到 底层流;来电者可能 希望刷新流 立即确保 接收构造函数 ObjectInputStreams 不会阻塞 阅读标题。
ObjectInputStream 构造函数: 创建一个从指定读取的 ObjectInputStream 输入流。序列化流 从流中读取标头,并且 已验证。这个构造函数会阻塞 直到对应 ObjectOutputStream 已写入并 刷新标题。
【讨论】:
你的意思是'所以构造 ObjectOutputStream 并刷新它'。【参考方案3】:除了在每次读取之前使用缓冲流和调用flush(),还应该先在两端创建ObjectOutputStream。此外,测试返回 null 的 readObject() 是没有意义的,除非您打算调用 writeObject(null)。使用 readObject() 对 EOS 的测试是 catch (EOFException exc)。
【讨论】:
我在研究另一个问题 (***.com/questions/26320156/…) 的答案时遇到了这个问题。令人费解的是 ObjectOutputStream 自己做了一些缓冲。所以也许是刷新(单独)在帮助......通过触发网络堆栈现在推送数据。 @StephenC 不,是创作,按照正确的顺序,是有帮助的。除非在堆栈的下方有缓冲的输出流,否则在头创建过程中没有缓冲。【参考方案4】:我仍然认为它会比这更快。我还能做些什么来改善这一点?
这似乎是一个微基准。它们总是很难正确处理,但我认为如果您在开始延迟测量之前先发送 2000 条消息。
另外,请参阅this 详细解答如何正确进行微基准测试。
【讨论】:
【参考方案5】:我希望您必须在双方都致电objectOutputStream.flush()
以确保数据立即发送到网络。否则,TCP 堆栈可能会等待一段时间以等待更多数据填充部分 IP 数据包。
【讨论】:
是的,我已经添加了这个并且没有做笔记。没有冲洗它根本无法工作。以上是关于使用带有套接字的 Java 对象流的性能问题的主要内容,如果未能解决你的问题,请参考以下文章
常问面试:String 对象在 JVM 如何存储提高性能的?