1、文件IO
使用 Off Heap 堆外内存,减少了堆内向堆外拷贝过程,效率要高一些,
堆内:JVM 堆内内存。
堆外:JVM堆外,JAVA 进程堆内。
mapped
映射:是 mmap
调用的一个进程和内核共享的内存区域,且这个内存区域是 PageCache 到文件的映射。
性能表现 mapped > off heap > on heep
。
Java 普通IO,每次调用写操作,都会向 OS PageCache 溢写,而 Buffered IO(8KB),向其写入时,仅写入到 JVM 内存,知道写满之后,才会溢写到 PageCache。
对比来看,Buffered 少了很多 system call
和 int 0x80
,因此效率高很多。
ByteBuffer
内存分配
- 堆内分配:
ByteBuffer.allocate(1024)
- 堆外分配:
ByteBuffer.allocateDirect(1024)
函数
-
put
:写入数据,并移动 position 指针。
-
flip
:由写模式切换到读模式。读模式下调用,会使 limit 指向当前 position 位置,造成指针错乱。
-
get
:读取数据,并移动 position 指针。读操作应在 flip 操作后执行,否则可能读取到错误数据。
-
compact
:压缩数据,并切换到写模式。如果在写模式调用,数据会错乱,因为 limit 指向 了 tail。
-
mark
-reset
:标记 position 位置,重置 position 到 mark 位置,不会切换读写模式,读写模式都可用。
-
slice
:创建一个新的字节缓冲区,其内容是给定缓冲区内容的共享子序列。新缓冲区的内容将从该缓冲区的当前位置开始。对该缓冲区内容的更改将在新缓冲区中可见,反之亦然。这两个缓冲区的位置,限制和标记值将是独立的。
新缓冲区的位置将为零,其容量和限制将为该缓冲区中剩余的浮点数,并且其标记将不确定。当且仅当该缓冲区是直接缓冲区时,新缓冲区才是直接缓冲区;当且仅当该缓冲区是只读缓冲区时,新缓冲区才是只读缓冲区。
rewind
:清除 mark 标记,并将 position 指针 重置为0。clear
:重置缓冲区,清除 mark 标记,重置 position 指针,重置 limit 指针,回归写模式。
RandomAccessFile
Mode | 说明 |
---|---|
r | 打开文件仅仅是为了读取数据,如果尝试调用任何写入数据的操作都会造成返回IOException错误信息的问题。 |
rw | 打开文件用于读写两种操作,如果文件本身并不存在,则会创建一个全新的文件。 |
rwd | 打开文件用于读写两种操作,这点和 rw 的操作完全一致,但是只会在 cache 满,或者调用 RandomAccessFile.close() 的时候才会执行内容同步操作。 |
rws | 在 rwd 的基础上对内容同步的要求更加严苛,每write修改一个byte都会直接修改到磁盘中。 |
RandomAccessFile raf = new RandomAccessFile(new File("/path/to/file"), "rw");
// 写数据
raf.write(new byte[5]);
// 移动指针
raf.seek(0L);
// 跳过字节
raf.skipBytes(1);
// 读数据 off:输入数组偏移量 len:读字节数
raf.read(new byte[10], 0, 10);
// 关闭并写盘
raf.close();
FileChannel
RandomAccessFile raf = new RandomAccessFile(new File("/path/to/file"), "rw");
FileChannel channel = raf.getChannel();
// Zero Copy
channel.transferTo(position, count, target);
channel.transferFrom(src, position, count);
MappedByteBuffer
- 不通过系统调用,数据直接到达 PageCache。
-
mmap
的内存映射,依然受内核 pageCache 体系所约束。 - DirectIO 是忽略内核的 PageCache,Java 没有直接使用内核 DirectIO 的支持,需要C程序JNI扩展库。
- MappedByteBuffer 只是交给程序开辟自己的字节数组充当 PageCache,仍需要程序动用代码维护一致性/Dirty等一系列问题。
MapMode | 说明 |
---|---|
FileChannel.MapMpde.READ_ONLY | Mode for a read-only mapping. 只读模式。 |
FileChannel.MapMpde.READ_WRITE | Mode for a read/write mapping. 读写模式。 |
FileChannel.MapMpde.PRIVATE | Mode for a private (copy-on-write) mapping. 堆内存更改不会写入文件。 |
RandomAccessFile raf = new RandomAccessFile(new File("/path/to/file"), "rw");
FileChannel channel = raf.getChannel();
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, 4090);
// 其余操作函数可参考 ByteBuffer
map.put(new byte[10]);
// PC数据写入存储设备
buffer.force();
2、TCP网络IO
服务端 LISTEN 状态端口号,最多65535个。
服务端无需为 Client 连接分配随机端口号,只要四元组(源IP、源端口、目标IP、目标端口)不同,就可以确定一个连接。
三次握手是内核级的,ServerSocket
启动之后,客户端连接时,无需等待 server.accept()
即可完成三次握手,建立连接。
因为客户端连接是连接层业务,不需要等待应用层响应。
客户端连接建立之后,server.accept()
之前,OS 仅仅开辟资源,并不会分配 FD。
当 server.accept()
之后,OS 才会创建 FD,FD 在 Java 中体现为 Socket
对象。
ServerSocket配置
BACK_LOG:未
accept
的连接队列长度,超过此数量的连接,在netstat
中体现为SYN_RECV
状态。SO_TIMEOUT:
server.accept()
超时配置,超过阈值未收到连接请求,会跑出SocketTimeoutException
异常,但并不会造成server
崩溃。RECEIVE_BUFFER_SIZE:最大接收窗口,默认 8192。最终数值由三次握手之后双方最小缓冲区为基准。
-
REUSE_ADDRESS:端口重用。如果禁用,在程序关闭后,端口可能会继续占用一段时间,如果此时立刻重启程序,会抛出异常。
绑定到相同端口的程序,必须全部启用该配置,才可重用端口,设置此参数必须在程序绑定到一个本地端口之前调用。
Socker配置
KEEP_ALIVE:TCP连接空闲时是否需要向对方发送探测包,依赖于底层的TCP模块实现的,Java中只能设置是否开启,不能设置其详细参数,依赖于系统配置。
SO_TIMEOUT:在与此
Socket
关联的InputStream
上调用read()
将只阻塞此时间长度,超时将引发SocketTimeoutException
,但socket
不会崩溃。-
OOB_INLINE:TCP的紧急指针,一般不建议使用,且不同的TCP/IP实现不同。
虽然
Socket.sendUrgentData()
的参数是int
类型,但只有这个int类型的低字节被发送,其它的三个字节被忽略。客户端和服务端必须同时开启,否则无法使用
Socket.sendUrgentData()
发送数据。 RECEIVE_BUFFER_SIZE:最大接收窗口,默认 8192。最终数值由三次握手之后双方最小缓冲区为基准。
SEND_BUFFER_SIZE:最大发送窗口,默认 8192。最终数值由三次握手之后双方最小缓冲区为基准。
-
REUSE_ADDRESS:端口重用。如果禁用,在程序关闭后,端口可能会继续占用一段时间,如果此时立刻重启程序,会抛出异常。
绑定到相同端口的程序,必须全部启用该配置,才可重用端口,设置此参数必须在程序绑定到一个本地端口之前调用。
-
SO_LINGER:控制 Socket 关闭行为,
- 默认情况执行
Socket.close()
立即返回,但底层的连接不会立即关闭,会延迟一段时间,直到数据发送完成,才会真正的关闭 Socket,断开连接。 -
Socket.setSoLinger(true, 0)
:执行Socket.close()
会立即返回,底层 Socket 立即关闭,所有未发送数据被丢弃。 -
Socket.setSoLinger(true, 3600)
:执行Socket.close()
会进入阻塞状态,底层 Socket 尝试发送剩余数据,直到如下条件解除阻塞状态:- 剩余数据发送完成。
- 剩余数据未发送完成,但阻塞超过 3600毫秒,立即返回,且丢弃剩余数据。
- 默认情况执行
TCP_NO_DELAY:默认
false
,为启用 Nagle算法。Nagle 算法的立意是,避免网络中充塞小封包,提高网络的利用率。