compact()用于在未读完数据时保留剩余内容并前移,重置position为剩余字节数、limit为capacity,为继续写入腾出空间;适用于粘包处理、分段读写等需复用缓冲区的场景。

Java NIO 中的 compact() 操作,是用来**在读模式后保留未读数据、腾出空间继续写入**的一种缓冲区整理方式。它不像 clear() 那样清空全部,而是“压缩”已读部分,把还剩的数据往前挪,让缓冲区能接着用 —— 特别适合边读边写、分段处理的场景(比如网络粘包拆包、文件分块读写)。
compact() 的实际效果
假设一个容量为 10 的 ByteBuffer,当前状态是:
- position = 4(已读/已写到第 4 个字节,下标 0~3 已处理)
- limit = 8(总共读到了 8 个字节,下标 0~7 有有效数据)
- capacity = 10
执行 buffer.compact() 后:
- 下标 4~7 的 4 个未读字节被复制到下标 0~3 的位置
- position 被设为 4(指向新数据末尾,可继续写入)
- limit 被设为 capacity(即 10),表示整个缓冲区现在都可用于写入
- mark 被丢弃(重置为 -1)
什么时候该用 compact()?
它不是万能清理操作,适用典型场景包括:
立即学习“Java免费学习笔记(深入)”;
- 从 Channel 读取数据后,只消费了部分(比如解析出半个消息),剩下字节要留着和下次读到的数据拼起来
- 使用
flip()进入读模式、调用若干次get()后,发现还有剩余字节没处理完,但又要往里写新数据 - 避免频繁分配新缓冲区,复用已有 buffer 提升性能(尤其在 Netty 或自研通信框架中常见)
和 clear()、flip() 的关键区别
三者用途不同,容易混淆:
- flip():仅切换模式——把 position 设为 0,limit 设为原 position,用于「写完后马上读」
- clear():彻底重置——position=0,limit=capacity,所有数据逻辑上被丢弃(实际数组内容还在,但不可见)
- compact():选择性整理——把 limit−position 个未读字节移到开头,position 更新为该数量,limit 变为 capacity
一句话记:flip 是“准备读”,clear 是“全清空再写”,compact 是“留下没读的,挪前面,接着写”。
注意事项与限制
使用 compact() 前需确认几点:
- 只对 可写的非只读缓冲区 有效;若调用在只读 buffer 上,会抛
ReadOnlyBufferException - 执行后原 position 到 limit 之间的数据被移动,但 capacity 不变,底层数组不扩容
- 如果当前 position == limit(即已读完全部数据),compact() 效果等价于 clear() —— 因为没有未读数据可搬
- 它不涉及堆外内存释放或 GC,只是数组内数据拷贝 + 状态重设


















