1. 什么是 I/O?

答:
I/O(Input/Output)即输入输出,是程序与外部设备(如磁盘、网络、键盘等)进行数据交换的过程。
在 Java 中,I/O 是以**流(Stream)**为基础进行串行化读写的,所有数据按顺序通过输入流或输出流传输。

典型场景:从服务器下载图片 → 网络流 → 内存 → 文件流 → 硬盘。


2. 同步 vs 异步、阻塞 vs 非阻塞的区别?

概念 含义 类比
同步(Synchronous) 调用后必须等待结果返回才能继续执行 打电话,必须等对方接通
异步(Asynchronous) 调用后立即返回,结果通过回调/通知获取 发微信,发完可以干别的事
阻塞(Blocking) 线程挂起,无法执行其他任务 钓鱼时一直盯着鱼竿
非阻塞(Non-blocking) 线程可继续执行其他任务 钓鱼时看书,偶尔看一眼鱼竿

⚠️ 注意:同步 ≠ 阻塞,异步 ≠ 非阻塞。它们描述的是不同维度。


3. 什么是 BIO(Blocking I/O)?

答:
BIO 是 同步阻塞 I/O 模型:

  • 客户端每发起一个连接请求,服务端就创建一个线程处理;
  • 在 I/O 操作完成前,线程被阻塞,不能做其他事;
  • 适用于连接数少且固定的场景(如传统企业应用);
  • JDK 1.4 之前唯一选择。

缺点: 并发量高时线程数爆炸,资源消耗大,上下文切换开销高。


4. 什么是 NIO(Non-blocking I/O)?

答:
NIO 是 同步非阻塞 I/O 模型(JDK 1.4 引入):

  • 基于 Channel(通道) + Buffer(缓冲区) + Selector(多路复用器)
  • 一个线程通过 Selector 轮询多个 Channel,只有当 Channel 有 I/O 事件时才处理;
  • 适用于高并发、短连接场景(如聊天服务器)。

核心组件:

  • ServerSocketChannel:监听连接
  • SocketChannel:读写数据
  • Selector:监听多个 Channel 的事件

5. 什么是 AIO(Asynchronous I/O)?

答:
AIO 是 异步非阻塞 I/O 模型(JDK 1.7 引入,也称 NIO.2):

  • 应用发起 I/O 请求后立即返回,操作系统在后台完成 I/O;
  • 完成后通过回调函数Future通知应用;
  • 适用于长连接、高吞吐场景(如文件服务器、相册服务)。

✅ 本质:由 OS 内核完成 I/O,应用无需轮询或阻塞。


6. 什么是 Netty?

答:
Netty 是一个高性能、异步事件驱动的网络应用框架,基于 NIO 封装:

  • 简化了 NIO 编程复杂度;
  • 提供丰富的协议支持(HTTP、WebSocket、Redis 等);
  • 内置线程模型、内存管理、心跳机制、流量控制等;
  • 被广泛用于 Dubbo、RocketMQ、Elasticsearch 等中间件。

7. BIO、NIO、AIO 的区别?

特性 BIO NIO AIO
模型 同步阻塞 同步非阻塞 异步非阻塞
线程模型 1 连接 = 1 线程 1 线程 = 多连接(Reactor) 1 请求 = 1 线程(Proactor)
数据读写 流(Stream) Buffer + Channel Future / Callback
适用场景 低并发、简单应用 高并发、短连接 高并发、长连接、大文件
编程难度 简单 复杂 更复杂

8. Java IO 流如何分类?

答:
数据单位

  • 字节流InputStream / OutputStream,处理二进制数据(图片、视频)
  • 字符流Reader / Writer,处理文本(自动编码转换)

流向

  • 输入流:从外部 → 内存
  • 输出流:从内存 → 外部

功能

  • 节点流:直接操作数据源(如 FileInputStream
  • 处理流:包装节点流,提供增强功能(如 BufferedInputStreamObjectOutputStream

9. 什么是内核空间?

答:
操作系统将内存分为:

  • 用户空间:应用程序运行区域,无权直接访问硬件;
  • 内核空间:OS 内核运行区域,拥有最高权限,可操作硬件(如磁盘、网卡)。

I/O 过程:用户程序 → 系统调用 → 内核空间读取数据 → 拷贝到用户空间。


10. 五种 I/O 模型是什么?

  1. 阻塞 I/O(BIO):线程阻塞直到数据就绪并拷贝完成。
  2. 非阻塞 I/O(NIO):线程轮询检查数据是否就绪。
  3. I/O 多路复用(select/poll/epoll):单线程监听多个 fd,任一就绪即处理(NIO 核心)。
  4. 信号驱动 I/O(SIGIO):内核就绪后发送信号通知应用。
  5. 异步 I/O(AIO):内核完成全部 I/O(包括拷贝)后通知应用。

✅ Linux 主流使用 epoll(多路复用),Windows 支持 IOCP(AIO)


11. Bit、Byte、Char 的区别?

单位 长度 说明
Bit(位) 1 bit 最小单位,值为 0 或 1
Byte(字节) 8 bits 存储单位,byte 类型范围 -128~127
Char(字符) 16 bits(UTF-16) 表示一个 Unicode 字符,范围 0~65535

💡 1 字符 = 2 字节(Java 中),但实际编码可能不同(如 UTF-8 中英文占 1~4 字节)。


12. 什么是对象序列化和反序列化?

答:

  • 序列化:将 Java 对象转换为字节流,便于存储或网络传输;
  • 反序列化:将字节流恢复为 Java 对象。

实现方式:

  • 类实现 Serializable 接口;
  • 不想序列化的字段加 transient 修饰。

13. serialVersionUID 的作用?

答:
serialVersionUID 是类的版本标识,用于反序列化时校验类是否兼容:

  • 若未显式定义,JVM 会根据类结构自动生成;
  • 类结构变化(如增删字段)会导致 UID 变化,反序列化失败(InvalidClassException);
  • 建议显式定义private static final long serialVersionUID = 1L;

14. 如何生成 serialVersionUID?

答:
两种方式:

  1. 固定值1L(适合不关心版本兼容)
  2. IDE 自动生成(推荐):
    • Eclipse/IDEA:提示后自动生成基于类结构的 hash 值;
    • 命令行:serialver com.example.User

15. BufferedReader 属于哪种流?常用方法?

答:

  • 属于字符处理流(包装 Reader);
  • 内部带缓冲区,减少磁盘 I/O 次数;
  • 经典方法
    • readLine():按行读取,返回 null 表示结束;
    • close():关闭流(会自动 flush)。

16. Java IO 流的顶级超类有哪些?

答: 四大抽象基类:

  • InputStream(字节输入)
  • OutputStream(字节输出)
  • Reader(字符输入)
  • Writer(字符输出)

17. 为什么图片/视频必须用字节流?

答:
因为这些文件是二进制数据,不是文本字符。
字符流会尝试按字符编码(如 UTF-8)解析,导致乱码或损坏;字节流原样传输,保证完整性。


18. BIO 服务端为何要多线程?

答:
BIO 中 serverSocket.accept()inputStream.read() 都是阻塞的:

  • 单线程下,处理一个客户端时,其他客户端无法连接;
  • 多线程:每个连接分配独立线程,避免相互阻塞。

19. 多线程 BIO 的瓶颈是什么?

答:

  • 线程数 = 连接数,高并发时线程爆炸;
  • 线程上下文切换开销大;
  • 内存占用高(每个线程栈约 1MB);
  • CPU 资源浪费在大量空闲 I/O 等待上。

20. 线程池能彻底解决 BIO 问题吗?

答:
不能。线程池只是限制线程数量,但:

  • 当并发连接 > 线程池大小时,新连接仍需排队;
  • 每个线程仍阻塞在 I/O 上,CPU 利用率低;
  • 本质仍是“1 线程 : 1 连接”,无法突破模型限制。

21. NIO 的核心组件有哪些?

答:

  • Buffer:数据容器(ByteBufferCharBuffer 等)
  • Channel:双向通道(FileChannelSocketChannel
  • Selector:多路复用器,监听多个 Channel 的事件
  • SelectionKey:表示 Channel 注册到 Selector 的状态(OP_READ/OP_WRITE 等)

22. NIO 中 Buffer 的重要属性?

答:
Buffer 有三个关键指针:

  • position:当前读写位置
  • limit:最多能读/写到的位置
  • capacity:缓冲区总容量

常用方法:flip()(切换读写模式)、clear()(重置指针)、compact()(压缩未读数据)。


23. Selector 的工作原理?

答:

  1. 将 Channel 注册到 Selector,指定关注事件(如 OP_ACCEPT);
  2. 调用 selector.select() 阻塞等待事件就绪;
  3. 获取就绪的 SelectionKey 集合;
  4. 根据事件类型(accept/read/write)分发处理;
  5. 处理完后移除 key,避免重复处理。

24. NIO 编程的难点是什么?

答:

  • 粘包/半包问题:TCP 是流协议,需自行拆包(如定长、分隔符、长度头);
  • Buffer 管理复杂:需手动 flip/clear,易出错;
  • 异常处理繁琐:如客户端断开需清理资源;
  • 多线程安全:Selector 非线程安全,需注意并发。

25. AIO 的核心类有哪些?

答:(JDK 1.7+)

  • AsynchronousServerSocketChannel:异步服务端通道
  • AsynchronousSocketChannel:异步客户端通道
  • 回调方式:
    • CompletionHandler:事件回调接口
    • Future:获取异步结果

26. Netty 的线程模型是什么?

答: 主从 Reactor 多线程模型

  • BossGroup:1 ~ N 个线程,负责 Accept 新连接;
  • WorkerGroup:N 个线程,负责处理 I/O 读写;
  • 每个 Channel 绑定到一个 EventLoop(线程),保证线程安全;
  • 业务逻辑可提交到业务线程池,避免阻塞 I/O 线程。

27. Netty 如何解决粘包/半包问题?

答: 提供多种解码器(Decoder):

  • FixedLengthFrameDecoder:定长消息
  • DelimiterBasedFrameDecoder:分隔符(如 \n
  • LengthFieldBasedFrameDecoder:长度字段(最常用)
  • 自定义 ByteToMessageDecoder

28. Netty 的零拷贝体现在哪里?

答: 三方面:

  1. 堆外内存(Direct Buffer):减少 JVM 堆内存拷贝;
  2. CompositeByteBuf:组合多个 Buffer,避免合并拷贝;
  3. FileRegion:文件传输使用 transferTo(),绕过用户态(内核态直传)。

29. Netty 的心跳机制如何实现?

答: 使用 IdleStateHandler

  • 添加到 Pipeline;
  • 配置读/写/全空闲超时时间;
  • 触发 userEventTriggered() 事件;
  • 在 Handler 中发送心跳包或关闭连接。

30. Netty 的 ChannelPipeline 是什么?

答:
ChannelPipeline责任链模式 的实现:

  • 每个 Channel 有一个 Pipeline;
  • 包含多个 ChannelHandler(Inbound/Outbound);
  • I/O 事件按顺序传递(如 decode → business → encode);
  • 支持动态添加/删除 Handler。

31. BIO、NIO、AIO 在 Linux 上的底层支持?

答:

  • BIO:基于 read() / write() 系统调用;
  • NIO:基于 select / poll / epoll(Linux 高性能用 epoll);
  • AIO:Linux 原生 AIO(io_uring 新模型)支持较弱,Windows IOCP 成熟。

⚠️ Java AIO 在 Linux 上实际是用线程池模拟的,并非真异步。


32. 为什么 Netty 不直接用 AIO?

答:

  • Linux 对 AIO 支持不完善,性能不如 epoll;
  • Netty 基于 NIO(epoll)已足够高效;
  • AIO 编程复杂,调试困难;
  • Netty 4.x 曾尝试 AIO,后因稳定性放弃。

33. 如何优化 Netty 性能?

答:

  • 使用 Epoll(Linux 下替换 NIO 为 Native Transport);
  • 合理设置 Boss/Worker 线程数(CPU 核数);
  • 使用 池化 ByteBuf(PooledByteBufAllocator);
  • 业务逻辑异步化,避免阻塞 EventLoop;
  • 启用 TCP 参数优化(SO_REUSEADDR, TCP_NODELAY 等)。

34. Netty 的内存泄漏如何排查?

答:

  • 开启 ResourceLeakDetector.setLevel(ADVANCED)
  • 观察日志中 LEAK 警告;
  • 确保每次 ByteBuf 使用后调用 release()
  • 使用 ReferenceCountUtil.release() 安全释放。

35. Netty 在哪些知名项目中使用?

答:

  • Dubbo:RPC 通信
  • RocketMQ:消息传输
  • Elasticsearch:节点通信
  • Cassandra:分布式数据库
  • gRPC-Java:底层网络层

总结

  • BIO:简单但低效,适合教学或低并发;
  • NIO:高并发基石,但编码复杂;
  • AIO:理论先进,落地受限;
  • Netty:工业级 NIO 封装,面试必问、项目首选
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐