
BufferOverflowException 本质是写入越界,不是内存溢出
这个异常和 JVM 堆内存溢出完全无关,它只发生在 java.nio.Buffer 子类(比如 ByteBuffer、CharBuffer)上,表示你试图往已满的缓冲区里继续 put() 数据。核心判断依据只有一个:buffer.position() + 写入字节数 > buffer.limit()。
常见错误现象包括:
- 调用
buffer.put(byte)或buffer.put(src)时直接抛出BufferOverflowException - 用
buffer.asCharBuffer().put("abc")时崩溃,但没意识到CharBuffer的limit是按字符数算,而底层ByteBuffer容量是按字节算 - 重复调用
buffer.flip()后忘记clear()或compact(),导致下一次写入时position没归零
写入前必须检查剩余空间:用 remaining() 而非 capacity()
capacity() 是缓冲区总大小,remaining() 才是当前还能写多少 —— 它等于 limit - position。很多 bug 就出在混淆这两者。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 每次
put()前先判断:if (buffer.remaining() - 批量写入时别硬编码长度,用
src.length或src.remaining()动态算,例如:buffer.put(src.array(), src.arrayOffset() + src.position(), src.remaining()) - 如果数据来源不可控(比如网络包),优先用
buffer.hasRemaining()配合循环,而不是一次性put()整块
flip() / clear() / compact() 的行为差异直接影响是否触发异常
这三个方法改变的是 position 和 limit,不改 capacity,但错用会直接让后续写入无空间。
典型场景与影响:
-
flip():把limit设为当前position,再把position归零 —— 这是为读取准备的,**之后不能再写,否则必抛BufferOverflowException** -
clear():重置position=0、limit=capacity,适合复用缓冲区写新数据 -
compact():把未读数据移到开头,position设在已复制数据末尾,limit设为capacity—— 适合“边读边写”的流式处理,但要注意它不会清空已读区域,残留数据可能干扰后续逻辑
Netty 或自建 NIO 服务中容易漏掉的检查点
在真实网络通信里,BufferOverflowException 往往暴露的是协议层设计问题,不是单纯代码错。
关键检查项:
- 接收缓冲区大小是否小于最大消息长度?比如用
ByteBuffer.allocate(1024)却没做分包处理,大包直接写崩 - 是否在
OP_READ回调里反复read()到同一个ByteBuffer,却忘了在每次read()前确认hasRemaining() - 使用
SocketChannel.write()时传入的ByteBuffer如果是只读的(asReadOnlyBuffer()),也会在某些 JDK 版本触发该异常,而非更明确的ReadOnlyBufferException - 跨线程共享缓冲区时,没有同步
position和limit修改,导致一个线程刚flip(),另一个线程就put()
缓冲区容量检查不是加个 if 就完事,它得嵌进整个数据生命周期里——从分配、填充、翻转、消费到复用,每一步的 position 和 limit 状态都得心里有数。最容易被忽略的,是多阶段协议解析中,不同步骤对同一缓冲区的 limit 设置相互覆盖。









