@@ -9115,7 +9115,7 @@ Java NIO 系统的核心在于:通道和缓冲区,通道表示打开到 IO
91159115
91169116缓冲区(Buffer):缓冲区本质上是一个**可以读写数据的内存块**,用于特定基本数据类型的容器,用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的
91179117
9118- 
9118+ 
91199119
91209120Buffer 底层是一个数组,可以保存多个相同类型的数据,根据数据类型不同 ,有以下 Buffer 常用子类:ByteBuffer, CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer
91219121
@@ -9166,6 +9166,7 @@ Buffer 基本操作:
91669166| public final int remaining() | 返回当前位置 position 和 limit 之间的元素个数 |
91679167| public final boolean hasRemaining() | 判断缓冲区中是否还有元素 |
91689168| public static ByteBuffer wrap(byte[] array) | 将一个字节数组包装到缓冲区中 |
9169+ | abstract ByteBuffer asReadOnlyBuffer() | 创建一个新的只读字节缓冲区 |
91699170
91709171Buffer 数据操作:
91719172
@@ -9249,7 +9250,7 @@ public class TestBuffer {
92499250
92509251##### 直接内存
92519252
9252- `byte buffer` 可以是两种类型 ,一种是基于直接内存(也就是非堆内存),另一种是非直接内存(也就是堆内存)。对于直接内存来说,JVM将会在IO操作上具有更高的性能,因为直接作用于本地系统的IO操作,而非直接内存,也就是堆内存中的数据,如果要作IO操作,会先从本进程内存复制到直接内存,再利用本地IO处理
9253+ Byte Buffer可以是两种类型 ,一种是基于直接内存(也就是非堆内存),另一种是非直接内存(也就是堆内存)。对于直接内存来说,JVM将会在IO操作上具有更高的性能,因为直接作用于本地系统的IO操作,而非直接内存,也就是堆内存中的数据,如果要作IO操作,会先从本进程内存复制到直接内存,再利用本地IO处理
92539254
92549255直接内存创建Buffer对象:`static XxxBuffer allocateDirect(int capacity)`
92559256
@@ -9258,7 +9259,77 @@ public class TestBuffer {
92589259* 非直接内存的作用链:本地IO-->直接内存-->非直接内存-->直接内存-->本地IO
92599260* 直接内存是:本地IO → 直接内存 → 本地IO
92609261
9261- JVM 内存结构详解直接内存
9262+ JVM直接内存详解
9263+
9264+ <img src="https://gitee.com/seazean/images/raw/master/Java/JVM-直接内存直接缓冲区.png" style="zoom: 50%;" />
9265+
9266+ <img src="https://gitee.com/seazean/images/raw/master/Java/JVM-直接内存非直接缓冲区.png" style="zoom:50%;" />
9267+
9268+
9269+
9270+ ****
9271+
9272+
9273+
9274+ ##### 共享内存
9275+
9276+ FileChannel 提供 map 方法把文件映射到虚拟内存,通常情况可以映射整个文件,如果文件比较大,可以进行分段映射
9277+
9278+ FileChannel 中的成员属性:
9279+
9280+ * MapMode.mode:内存映像文件访问的方式,共三种:
9281+ * MapMode.READ_ONLY:只读,试图修改得到的缓冲区将导致抛出异常。
9282+ * MapMode.READ_WRITE:读/写,对得到的缓冲区的更改最终将写入文件;但该更改对映射到同一文件的其他程序不一定是可见的
9283+ * MapMode.PRIVATE:私用,可读可写,但是修改的内容不会写入文件,只是 buffer 自身的改变,称之为 copy on write 写时复制
9284+
9285+ * position:文件映射时的起始位置
9286+ * `public final FileLock lock()`:获取此文件通道的排他锁
9287+
9288+ MappedByteBuffer,可以让文件直接在内存(堆外内存)中进行修改,这种方式叫做内存映射,可以直接调用系统底层的缓存,没有JVM和系统之间的复制操作,提高了传输效率,作用:
9289+
9290+ * 用在进程间的通信,能达到**共享内存页**的作用,但在高并发下要对文件内存进行加锁,防止出现读写内容混乱和不一致性,Java 提供了文件锁 FileLock,但在父/子进程中锁定后另一进程会一直等待,效率不高
9291+ * 读写那些太大而不能放进内存中的文件
9292+
9293+ MappedByteBuffer 较之 ByteBuffer新增的三个方法
9294+
9295+ - `final MappedByteBuffer force()`:缓冲区是 READ_WRITE 模式下,对缓冲区内容的修改强行写入文件
9296+ - `final MappedByteBuffer load()`:将缓冲区的内容载入物理内存,并返回该缓冲区的引用
9297+ - `final boolean isLoaded()`:如果缓冲区的内容在物理内存中,则返回真,否则返回假
9298+
9299+ ```java
9300+ public class MappedByteBufferTest {
9301+ public static void main(String[] args) throws Exception {
9302+ RandomAccessFile ra = new RandomAccessFile("1.txt", "rw");
9303+ //获取对应的通道
9304+ FileChannel channel = ra.getChannel();
9305+
9306+ /**
9307+ * 参数1 FileChannel.MapMode.READ_WRITE 使用的读写模式
9308+ * 参数2 0: 可以直接修改的起始位置
9309+ * 参数3 5: 是映射到内存的大小(不是索引位置),即将 1.txt 的多少个字节映射到内存
9310+ * 可以直接修改的范围就是 0-5
9311+ * 实际类型 DirectByteBuffer
9312+ */
9313+ MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
9314+
9315+ buffer.put(0, (byte) 'H');
9316+ buffer.put(3, (byte) '9');
9317+ buffer.put(5, (byte) 'Y'); //IndexOutOfBoundsException
9318+
9319+ ra.close();
9320+ System.out.println("修改成功~~");
9321+ }
9322+ }
9323+ ```
9324+
9325+ 从硬盘上将文件读入内存,要经过文件系统进行数据拷贝,拷贝操作是由文件系统和硬件驱动实现。通过内存映射的方法访问硬盘上的文件,拷贝数据的效率要比 read 和 write 系统调用高:
9326+
9327+ - read() 是系统调用,首先将文件从硬盘拷贝到内核空间的一个缓冲区,再将这些数据拷贝到用户空间,实际上进行了两次数据拷贝
9328+ - map() 也是系统调用,但没有进行数据拷贝,当缺页中断发生时,直接将文件从硬盘拷贝到用户空间,只进行了一次数据拷贝
9329+
9330+
9331+
9332+ 参考文章:https://www.jianshu.com/p/f90866dcbffc
92629333
92639334
92649335
@@ -9383,6 +9454,8 @@ Channel 的两个方法:
938394541. Buffer
938494552. 使用上述两种方法
93859456
9457+ 
9458+
93869459```java
93879460public class ChannelTest {
93889461 @Test
@@ -9500,7 +9573,7 @@ public class ChannelTest {
95009573
95019574选择器(Selector) 是 SelectableChannle 对象的**多路复用器**,Selector 可以同时监控多个通道的状况,利用 Selector 可使一个单独的线程管理多个 Channel。**Selector 是非阻塞 IO 的核心**。
95029575
9503- 
9576+ 
95049577
95059578* Selector 能够检测多个注册的通道上是否有事件发生(多个 Channel 以事件的方式可以注册到同一个Selector),如果有事件发生,便获取事件然后针对每个事件进行相应的处理,就可以只用一个单线程去管理多个通道,也就是管理多个连接和请求
95069579* 只有在连接/通道真正有读写事件发生时,才会进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程
@@ -9749,8 +9822,6 @@ AsynchronousSocketChannel、AsynchronousServerSocketChannel、AsynchronousFileCh
97499822
97509823
97519824
9752-
9753-
97549825***
97559826
97569827
0 commit comments