高性能IO核心是绕开用户态搬运:优先用transferTo/transferFrom零拷贝;大文件随机读写用MappedByteBuffer;自定义处理时复用DirectBuffer并严格管理position/limit。

核心在于让数据尽量不经过 JVM 堆内存中转,减少复制次数和 GC 压力。真正高性能的传输不是“用不用 Channel”,而是“是否绕开了用户态搬运”。
优先走零拷贝:transferTo/transferFrom
这是最高效的方式——数据全程在内核空间流动,不进用户内存。
- 源必须是本地 FileChannel(不能是加密流、管道或 stdin)
- 目标要是 SocketChannel(如 HTTP 下载)或同文件系统的 FileChannel(如文件复制)
- 调用前检查
sourceChannel.size(),避免 position 或 count 超限导致降级为堆内拷贝 - Linux 支持 sendfile(),macOS 不支持,会自动 fallback 到普通 read/write,性能明显下降
大文件随机读写:用 MappedByteBuffer 内存映射
适合数据库索引、日志检索等需频繁跳转位置的场景。
- 调用
channel.map(READ_ONLY, offset, size)直接把文件某段映射为 JVM 可访问的直接内存 - 无需 read(),可像操作数组一样 get()/put(),OS 按需分页加载,延迟低
- 注意:映射后若外部程序修改文件,可能触发
IOException;超大文件长期映射易引发 OOM - 写入后需调用
force()才能确保落盘(如需强持久化)
需要自定义处理时:复用 DirectBuffer + 手动 read/write
当要加压缩、加密、校验或监控进度时,零拷贝不可用,但依然可以高效。
- 用
ByteBuffer.allocateDirect(8192)分配堆外缓冲区,避免每次 write() 前隐式复制到临时 direct 内存 - 不要在循环里反复 new DirectBuffer,应池化复用,配合
clear()/flip() -
read()后必须flip()(设 limit = position,position = 0),否则get()读不到数据 - 一次读写完成后用
clear()重置,别用compact()除非你明确要保留未读完内容
避坑要点:Buffer 状态管理是高频丢数根源
Buffer 不是智能容器,position 和 limit 错位会导致静默失败。
- 典型错误:
channel.read(buf)返回 1024 字节后,忘记flip()就直接传给解析逻辑 → 实际只读到 0 字节 - 调试技巧:打印
buf.toString()查看当前 position/limit/cap;heap buffer 可用Arrays.toString(buf.array())看真实内容 - 小数据、单次 header 写入:用 heap buffer 更轻量;高频复用、长连接缓冲池:必须用 direct buffer


















