Skip to content

Commit ced6946

Browse files
committed
Update Java Notes
1 parent e5c1a27 commit ced6946

File tree

1 file changed

+77
-6
lines changed

1 file changed

+77
-6
lines changed

Java.md

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9115,7 +9115,7 @@ Java NIO 系统的核心在于:通道和缓冲区,通道表示打开到 IO
91159115

91169116
缓冲区(Buffer):缓冲区本质上是一个**可以读写数据的内存块**,用于特定基本数据类型的容器,用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的
91179117

9118-
![](https://gitee.com/seazean/images/raw/master//NIO-Buffer.png)
9118+
![](https://gitee.com/seazean/images/raw/master/Java/NIO-Buffer.png)
91199119

91209120
Buffer 底层是一个数组,可以保存多个相同类型的数据,根据数据类型不同 ,有以下 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

91709171
Buffer 数据操作:
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 的两个方法:
93839454
1. Buffer
93849455
2. 使用上述两种方法
93859456

9457+
![](https://gitee.com/seazean/images/raw/master/Java/NIO-复制文件.png)
9458+
93869459
```java
93879460
public class ChannelTest {
93889461
@Test
@@ -9500,7 +9573,7 @@ public class ChannelTest {
95009573

95019574
选择器(Selector) 是 SelectableChannle 对象的**多路复用器**,Selector 可以同时监控多个通道的状况,利用 Selector 可使一个单独的线程管理多个 Channel。**Selector 是非阻塞 IO 的核心**。
95029575

9503-
![](https://gitee.com/seazean/images/raw/master//NIO-Selector.png)
9576+
![](https://gitee.com/seazean/images/raw/master/Java/NIO-Selector.png)
95049577

95059578
* Selector 能够检测多个注册的通道上是否有事件发生(多个 Channel 以事件的方式可以注册到同一个Selector),如果有事件发生,便获取事件然后针对每个事件进行相应的处理,就可以只用一个单线程去管理多个通道,也就是管理多个连接和请求
95069579
* 只有在连接/通道真正有读写事件发生时,才会进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程
@@ -9749,8 +9822,6 @@ AsynchronousSocketChannel、AsynchronousServerSocketChannel、AsynchronousFileCh
97499822

97509823

97519824

9752-
9753-
97549825
***
97559826

97569827

0 commit comments

Comments
 (0)