0. 结论
先说结论吧。
- jdk8和以前,java nio的windows实现,在底层是基于winsock2的select。
- 但是winsock2的select是否是基于轮询的,是不是我们常说的select/poll/epoll中的select,我无法查证,毕竟windows不是开源的。如果是轮询,那效率是相当低的。所以说windows就这点不好>_<。
- 一次select可返回的最大数量是1024。
1. 在windows上的实现
参考java nio 在windows上的实现
很多人说是IOCP,其实是select。
首先我们一步步查看调用链:
Selector.select->...->WindowsSelectorImpl.doSelect->WindowsSelectorImpl.SubSelector.poll->WindowsSelectorImpl.SubSelector.poll0
我们看下WindowsSelectorImpl.SubSelector.poll:
private int poll() throws IOException {
return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress, Math.min(WindowsSelectorImpl.this.totalChannels, 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout);
}
然后再看最后调用的WindowsSelectorImpl.SubSelector.poll0:
private native int poll0(long var1, int var3, int[] var4, int[] var5, int[] var6, long var7);
该函数是一个native方法,其源码需要到jdk源码中查看。但jdk不是开源的,所以native方法实现只能在openJdk中找。
参考OpenJDK与JDK的区别分析,openJdk只是在jdk上加了少许不痛不痒的功能而已,关于WindowsSelectorImpl的实现应该是一样的。
接下来我们找到openJdk的源码,查看winsock2$poll0
2. WindowsSelectorImpl
我们找到openJdk8的源码jdk8 WindowsSelectorImpl.c.shtml
2.1 调用winsock2$select
参考:
在WindowsSelectorImpl$poll0函数中我们看到:
if ((result = select(0 , &readfds, &writefds, &exceptfds, tv))
== SOCKET_ERROR)
所以调用的是winsock2的select
方法,阻塞监听触发read/write事件的fd(这里的fd就是指socket)。激活读写事件的socket会保存在readfds和writefds中。
所以:
- jdk8和以前,java nio的windows实现,在底层是基于winsock2的select。
- 但是winsock2的select是否是基于轮询的,是不是我们常说的select/poll/epoll中的select,我无法查证,毕竟windows不是开源的。如果是轮询,那效率是相当低的。所以说windows就这点不好>_<。
2.2 设置上层调用的返回值
之后有如下代码:
resultbuf[0] = readfds.fd_count;
for (i = 0; i < (int)readfds.fd_count; i++) {
resultbuf[i + 1] = (int)readfds.fd_array[i];
}
(*env)->SetIntArrayRegion(env, returnReadFds, 0, readfds.fd_count + 1, resultbuf);
该段代码的作用,是把激活读事件的socket拷贝至returnReadFds
中,作为上层调用的返回值。
附近格式类似的代码功能也一样,都是把激活read/write事件的socket拷贝到returnReadFds/returnWriteFds中,作为上层调用的返回值。如果有异常发生,则returnExceptFds
也会被正确地被赋值。
2.3 selector的最大监听数
根据上一节,我们发现在poll0调用中,会传入参数this.readFds, this.writeFds, this.exceptFds
,以便poll0设置返回值:
private int poll() throws IOException {
return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress, Math.min(WindowsSelectorImpl.this.totalChannels, 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout);
}
其中this.readFds
是成员函数,我们看到定义:
private final class SubSelector {
private final int pollArrayIndex;
private final int[] readFds;
private final int[] writeFds;
private final int[] exceptFds;
以及其构造函数:
![](https://img.haomeiwen.com/i7547741/21cc01f8429619bc.png)
可以发现,readFds、writeFds、exceptFds都被赋予了固定长度的数组, 而且这个变量并没有再变换过。
readFds、writeFds、exceptFds的作用是接收触发相应事件的socket。
所以我认为,在windows中,selector一次监听返回的最大激活数目不能超过1024。
不过在winsock2.h中,我看到:
typedef struct fd_set {
u_int fd_count; /* how many are SET? */
SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;
其中fd_set是returnReadFds、returnWriteFds的类型,也就是说fd_set是返回值参数的类型。
然后FD_SETSIZE的长度:
#ifndef FD_SETSIZE
#define FD_SETSIZE 64
#endif /* FD_SETSIZE */
意思非常明显,也就是说winsock默认最多处理64个sock。也可以通过#define
修改FD_SETSIZE
为想要的值。
我们回看jdk8的WindowsSelectorImpl.c,在开头就有一句:
#define FD_SETSIZE 1024
说明openJdk将这个限制改成了1024,与上文的数值一致。最后,我们再一次确认了这个限制是1024。
网友评论