使用带有套接字的 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 如何存储提高性能的?

Java性能优化之使用NIO提升性能

java 8顺序流有直接或间接的性能优势吗?

java基础30问

(有没有)python socketserver 相对于常规套接字对象的性能优势?

RPC、套接字和性能注意事项