【问题标题】:Java Non-blocking IO CPU LeakJava 非阻塞 IO CPU 泄漏
【发布时间】:2013-08-01 00:38:37
【问题描述】:

我写了简单的 NIO 服务器:

public static void main(String[] args) throws Exception
{
    ByteBuffer buffer = ByteBuffer.allocateDirect(65536);
    Selector selector = Selector.open();
    ServerSocketChannel server = ServerSocketChannel.open();
    server.configureBlocking(false);
    server.bind(new InetSocketAddress(724));
    server.register(selector, SelectionKey.OP_ACCEPT);
    while(server.isOpen())
    {
        selector.selectNow();
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while(iterator.hasNext())
        {
            SelectionKey key = iterator.next();
            iterator.remove();
            if(!key.isValid())
            {
                System.out.println("Invalid key removed!");
                key.channel().close();
                key.cancel();
                continue;
            }
            if(key.isAcceptable())
            {
                server.accept().configureBlocking(false).register(selector, SelectionKey.OP_READ);
                System.out.println("Accepting channel...");
                continue;
            }
            if(key.isReadable())
            {
                SocketChannel channel = (SocketChannel) key.channel();
                try
                {
                    channel.read((ByteBuffer) buffer.clear());
                    if(buffer.flip().limit() == 0)
                    {
                        continue;
                    }
                    System.out.println(buffer.get() & 0xFF);
                } catch(Exception e)
                {
                    e.printStackTrace();
                    channel.close();
                    key.cancel();
                }
            }
        }
    }
}

它是客户:

public static void main(String[] args) throws Exception
{
    try(Socket socket = new Socket("localhost", 724))
    {
        OutputStream output = socket.getOutputStream();
        output.write(15);
        output.flush();
    }
}

一旦客户端与服务器断开连接,CPU 的负载会增加高达 40%

我试图找到解决方案,但搜索没有给出任何结果。 我认为这是由于选择器没有删除断开客户端的事实,但我不知道如何测试 - 客户端是否断开连接。 channel.isOpen() 总是返回 true。

【问题讨论】:

  • 您是否尝试过使用VisualVM 进行分析?这可能会准确地告诉您系统将时间花在哪里。
  • 编辑:大部分 CPU 时间花在 java.net.SocketInputStream.read(byte[], int, int);
  • 刚刚关闭频道的用户不需要取消key。

标签: java asynchronous cpu nio memory-leaks


【解决方案1】:

来自Javadoc for Selector.selectNow()

此方法执行非阻塞选择操作。如果自上次选择操作以来没有通道成为可选通道,则此方法立即返回零。

是的,这会烧 CPU;它是非阻塞的,你有一个紧密的循环。

编辑添加: 通常,使用Selector 背后的想法是您使用阻塞的select()select(long timeout) 版本,这意味着一旦套接字执行某些操作,它就会解除阻塞。

【讨论】:

  • Selector.select() 也是如此。
  • 我测试了 Selector.select() 和 Selector.select(timeout),结果一样。
  • 在哪里测试过?你做了什么不同的事情?使用select() 需要对代码进行大量更改。
  • 你能告诉我怎么做吗?我不知道如何以不同的方式做到这一点
  • @user1936245 如果你不知道怎么做,为什么说你已经做了?或者,如果你已经做到了,你为什么说你不知道怎么做?
【解决方案2】:

找到解决方案。收到 EOS 后我忘记关闭连接了。

if(channel.read((ByteBuffer) buffer.clear()) == -1)
{
    channel.close();
    continue;
}

【讨论】:

  • 这不会关闭连接。它只是取消密钥。你是正确的,你应该关闭 EOS 上的连接,但你没有这样做。
  • 我重复一遍。关闭频道后无需取消密钥。为你修好了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-18
  • 1970-01-01
  • 2012-09-03
  • 1970-01-01
  • 2010-11-17
相关资源
最近更新 更多