factory方法仅创建线程配置模板,零开销、不分配内存、不触发计算;真正触发内存分配与计算的是start()、submit()、read()等调度或执行动作。

调用 factory 方法本身不会触发任何内存计算,它只是创建一个线程配置模板,零开销、不分配堆外内存、不初始化缓冲区、也不执行业务逻辑。所谓“调用时才真正触发计算”,实则是对执行时机的误读——真正决定内存分配与计算发生的,是后续的 start()、submit()、execute() 或 publishOn() 等调度动作。
factory 本质是线程规格说明书,不是执行入口
像 Thread.ofVirtual().factory()、Executors.defaultThreadFactory() 或 Netty 的 DefaultThreadFactory,返回的只是一个可复用的对象,仅封装以下元信息:
- 线程名前缀(如 "nio-event-loop-")
- 是否守护线程(daemon)
- 线程优先级(priority)
- 未捕获异常处理器(UncaughtExceptionHandler)
它不 new 任何 ByteBuffer,不调用 allocateDirect(),也不触发 JVM 堆外内存分配。哪怕你反复调用 factory.newThread(runnable) 十次,只要没 start(),就没有任何线程启动,更无内存计算发生。
内存计算的真实触发点在执行层
Java 高级 IO 框架(如 Netty、WebFlux、Vert.x)中,真正触发内存分配和计算的环节集中在以下三类操作:
-
Channel 注册后首次 read/write:NIO 的
ByteBuffer.allocateDirect(8192)发生在SelectionKey.OP_READ就绪后,由NioEventLoop调用unsafe.read()时触发,而非 factory 创建时 -
任务提交到线程池:比如
businessExecutor.submit(() -> { db.query(...); }),此时才通过 factory 构建线程,并在线程首次执行时加载类、初始化连接池、分配临时 buffer -
响应式流下游订阅:Mono/Flux 中的
map()、flatMap()内部逻辑,只在subscribeOn(scheduler)绑定的 scheduler 执行时才真正运行,包括其中的new byte[4096]或Files.readAllBytes()
如何一眼识破“假延迟”?盯住三处代码
判断某段内存或计算是否真被延迟到“调用时”,不要看 factory,而要看被调度的具体逻辑体:
- 构造器里有没有 allocateDirect / new MappedByteBuffer / loadBigConfig()?有则属于“一造就占内存”,和 factory 无关
-
关键方法体内是否含 I/O 或大数组分配?例如
encode(HttpRequest req)里调用了Unpooled.directBuffer(),那这个 buffer 就是在 encode 被调用那一刻才分配 -
是否有显式 lazy 初始化块?如
private volatile ByteBuffer buf; ... if (buf == null) buf = allocateDirect();,这才是真正的按需内存计算
高级框架为何要这样设计?
这种“定义与执行分离”的架构,支撑了两个核心能力:
- 按需内存保压:万级连接共用一个 EventLoop,每个 Channel 的读写 buffer 只在数据真正到达时才分配,避免堆外内存爆炸
- 执行平面隔离:I/O 线程只做 socket 读写和事件分发;业务计算(含内存分配)被推送到独立线程池,防止阻塞 selector 循环
所以 factory 不是“开关”,而是“模具”;真正按下启动键的,永远是那个 submit()、那个 read()、那个 subscribe()。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











