当前位置:   article > 正文

java nio epoll bug_JAVA NIO存在的问题

java nio bug

JAVA 包含最新的版本JDK1.8的NIO存在一些问题,这些问题需要在编写NIO程序时要格外关注:

NIO跨平台和兼容性问题

NIO是底层API,它的实现依赖于操作系统针对IO操作的APIs. 所以JAVA能在所有操作系统上实现统一的接口,并用一致的行为来操作IO是很伟大的。

使用NIO会经常发现代码在Linux上正常运行,但在Windows上就会出现问题。所以编写程序,特别是NIO程序,需要在程序支持的所有操作系统上进行功能测试,否则你可能会碰到一些莫明的问题。

NIO2看起来很理想,但是NIO2只支持Jdk1.7+,若你的程序在Java1.6上运行,则无法使用NIO2。另外,Java7的NIO2中没有提供DatagramSocket的支持,所以NIO2只支持TCP程序,不支持UDP程序

NIO对缓冲区的聚合和分散操作可能会导致内存泄露

很多Channel的实现支持Gather和Scatter。这个功能允许从从多个ByteBuffer中读入或写入,这样做可以有更好的性能。

操作系统底层知道如何处理这些被写入/读出,并且能以最有效的方式处理。如果要分割的数据在多个不同的ByteBuffer中,使用Gather/Scatter是比较好的方式。

例如,你可能希望header在一个ByteBuffer中,而body在另外的ByteBuffer中;

下图显示的是Scatter(分散),将ScatteringByteBuffer中的数据分散读取到多个ByteBuffer中:

320c70afb5a7f4f49837fb67979bcb29.png

下图显示的是Gather(聚合),将多个ByteBuffer的数据写入到GatheringByteChannel:

9be18b7697c988aa4b5a958f82f37600.png

可惜Gather/Scatter功能会导致内存泄露,知道Java7才解决内存泄露问题。使用这个功能必须小心编码和Java版本

Squashing the famous epoll bug(压碎著名的epoll bug)

Linux-like OSs的选择器使用的是epoll-IO事件通知工具。操作系统使用这一高性能的技术与网络协议栈异步工作。

不幸的是,即使是现在,著名的epoll-bug也可能会导致无效的状态选择和100%的CPU利用率。要解决epoll-bug的唯一方法是回收旧的选择器,将先前注册的通道实例转移到新创建的选择器上。

NIO中对epoll问题的解决方案是有限制的,Netty提供了更好的解决方案。下面是epoll-bug的一个例子

import java.io.IOException;

import java.net.InetSocketAddress;

import java.net.ServerSocket;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.ServerSocketChannel;

import java.nio.channels.SocketChannel;

import java.util.Iterator;

import java.util.Set;

public class PlainNioEchoServer {

public static void main(String[] args) throws IOException {

serve(8118);

}

public static void serve(int port) throws IOException {

System.out.println("Listening for connections on port " + port);

ServerSocketChannel serverChannel = ServerSocketChannel.open();

ServerSocket ss = serverChannel.socket();

InetSocketAddress address = new InetSocketAddress(port);

ss.bind(address);

serverChannel.configureBlocking(false);

Selector selector = Selector.open();

serverChannel.register(selector, SelectionKey.OP_ACCEPT);

while (true) {

try {

// 这里发生的是,不管有没有已选择的SelectionKey,Selector.select()方法总是不会阻塞并且会立刻返回。

// 这违反了Javadoc中对Selector.select()方法的描述,

// Javadoc中的描述:Selector.select() must not unblock if nothing is selected.

// (Selector.select()方法若未选中任何事件将会阻塞。)

System.out.println(".............");

selector.select();

}

catch (IOException ex) {

ex.printStackTrace();

// handle in a proper way

break;

}

Set readyKeys = selector.selectedKeys();

Iterator iterator = readyKeys.iterator();

// 该值将永远是假的,代码将持续消耗你的CPU资源。

//这会有一些副作用,因为CPU消耗完了就无法再去做其他任何的工作。

while (iterator.hasNext()) {

SelectionKey key = (SelectionKey) iterator.next();

iterator.remove();

try {

if (key.isAcceptable()) {

ServerSocketChannel server = (ServerSocketChannel) key.channel();

SocketChannel client = server.accept();

System.out.println("Accepted connection from " + client);

client.configureBlocking(false);

client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ,

ByteBuffer.allocate(100));

}

if (key.isReadable()) {

SocketChannel client = (SocketChannel) key.channel();

ByteBuffer output = (ByteBuffer) key.attachment();

client.read(output);

}

if (key.isWritable()) {

SocketChannel client = (SocketChannel) key.channel();

ByteBuffer output = (ByteBuffer) key.attachment();

output.flip();

client.write(output);

output.compact();

}

}

catch (IOException ex) {

key.cancel();

try {

key.channel().close();

}

catch (IOException cex) {

}

}

}

}

}

}

运行程序后,客户端连接进来,什么工作都不做,但CPU利用率却已经达到100%

5c26dbc5a3ad55eebd467e739f801a32.png

这些仅仅是在使用NIO时可能会出现的一些问题。不幸的是,虽然在这个领域发展了多年,问题依然存在;

幸运的是,Netty给了你比较好的解决方案。

参考

《Netty in Action》

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/604107
推荐阅读
相关标签
  

闽ICP备14008679号