[TOC]
> 在开始之前,我觉得有必要充分理解一下缓冲区的作用及使用方法。
## 一、缓冲区基础
在NIO技术的缓冲区中,存在4个核心技术点,分别是:
❑capacity(容量)
❑limit(限制)
❑position(位置)
❑mark(标记)
这4个技术点之间值的大小关系如下:0≤mark≤position≤limit≤capacity。
由于ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer和ShortBuffer是抽象类,wrap()就相当于创建这些缓冲区的工厂方法。最终对应的类型分别为`java.io.HeapByteBuffer`、`java.io.HeapCharBuffer`、`java.io.HeapDoubleBuffer`等。
### limit
限制(limit)代表第一个不应该读取或写入元素的index,缓冲区的limit不能为负,并且limit不能大于其capacity。如果position大于新的limit,则将position设置为新的limit。如果mark已定义且大于新的limit,则丢弃该mark。
![](https://img.kancloud.cn/34/aa/34aaea743ca4efa39c9942ba0b5e1850_386x153.png)
```java
public class BufferTest {
@Test
public void bufferLimitTest(){
char[] charArray = new char[]{'a','b','c','d','e'};
CharBuffer buffer = CharBuffer.wrap(charArray);
System.out.println("A capacity()=" + buffer.capacity() + ",limit="+buffer.limit());
buffer.limit(3);
System.out.println("B capacity()=" + buffer.capacity() + ",limit="+buffer.limit());
buffer.put(0,'o');
buffer.put(1,'p');
buffer.put(2,'q');
buffer.put(3,'r');//index == 3,第一个不可读不可写的索引
buffer.put(4,'s');
buffer.put(5,'t');
buffer.put(6,'u');
}
}
```
### position(位置)
什么是位置呢?它代表“下一个”要读取或写入元素的index(索引),缓冲区的position(位置)不能为负,并且position不能大于其limit。如果mark已定义且大于新的position,则丢弃该mark。
![](https://img.kancloud.cn/cf/f6/cff65cd16f6f506d1565fecbd589c82f_267x105.png)
```java
@Test
public void bufferLimitTest(){
char[] charArray = new char[]{'a','b','c','d','e'};
CharBuffer buffer = CharBuffer.wrap(charArray);
System.out.println("A capacity()=" + buffer.capacity() + ",limit="+buffer.limit()+ ",position="+buffer.position());
buffer.limit(3);
System.out.println("B capacity()=" + buffer.capacity() + ",limit="+buffer.limit() + ",position="+buffer.position());
buffer.put(0,'o');
buffer.put(1,'p');
buffer.put(2,'q');
buffer.put(3,'r');//index == 3,第一个不可读不可写的索引
buffer.put(4,'s');
buffer.put(5,'t');
buffer.put(6,'u');
}
```
> 运行结果如下:
> A capacity()=5,limit=5,position=0
> B capacity()=5,limit=5,position=2
> a
> b
> Z
> d
> e
### mark(标记)
标记有什么作用呢?缓冲区的标记是一个索引,在调用reset()方法时,会将缓冲区的position位置重置为该索引。标记(mark)并不是必需的。定义mark时,不能将其定义为负数,并且不能让它大于position。
- 如果定义了mark,则在将position或limit调整为小于该mark的值时,该mark被丢弃,丢弃后mark的值是-1。
- 如果未定义mark,那么调用reset()方法将导致抛出`InvalidMarkException`异常。
简而言之,buffer的reset()方法,可以根据mark()的位置,把位置重置为mark()调用时的位置。
其他的一些关系:
- 如果position大于新的limit,则position的值就是新limit的值。
## 二、非直接缓冲区和直接缓冲区
### 非直接缓冲区
![](https://img.kancloud.cn/05/17/0517591f49a57e9acf20783377ac5d2c_457x115.png)
通过ByteBuffer向硬盘存取数据时是需要将数据暂存在JVM的中间缓冲区,如果有频繁操作数据的情况发生,则在每次操作时都会将数据暂存在JVM的中间缓冲区,再交给ByteBuffer处理,这样做就大大降低软件对数据的吞吐量,提高内存占有率,造成软件运行效率降低,这就是非直接缓冲区保存数据的过程,所以非直接缓冲区的这个弊端就由直接缓冲区解决了。
### 直接缓冲区
![](https://img.kancloud.cn/68/85/68856c6847845e661fccd07bf19c250f_508x127.png)
- 第一章 开篇寄语
- 1-1 技术选型要点
- 1-2 认识905.4王国的交流规范
- 1-3 联系作者
- 第二章 Socket编程的基础知识
- 2-1 Socket家族的基石
- 2-2 byte数组基础
- 2-3 缓冲区基础
- 2-4 NIO Socket通讯的工作原理
- 第三章 905.4规范解读
- 3-1 基于通道选择器的Socket长连接及消息读写框架
- 3-2 严格的信件收发员
- 3-3 负责消息处理的一家子
- 3-4 负责认证的大儿子(AuthWorker)
- 3-5 哑巴老二(PingWoker)
- 3-6 勤奋的定位汇报员老三(LocationReportWorker)
- 3-7 精明的老四(BusinessReportWorker)
- 3-8 数据检察官——CRC16-CCITT校验
- 3-11 数据的加密官
- 3-12 头尾标识转义
- 第四章 测试方法
- 4-1 测试数据样例
- 4-2 客户端链路保持功能实现
- 4-3 使用Socket短连接进行功能测试
- 4-4 NIO服务端性能分析
- 4-5 http测试方法(推荐)
- 第五章 从NIO到netty
- 5-1 编程进阶——Netty核心基础
- 5-2 Netty使用常见问题
- 5-3 使用Netty重写Server端
- 5-4 Netty之链路管理
- 5-5 netty堆外内存泄漏如何应对?
- 第六章 统计与监控
- 6-1 Grafana监控面板
- 第七章 售后服务
- 7-1 勘误与优化
- 7-2 获取源码