Kotlin测试协程与线程差异 启动百万线程未触发OOM问题咨询
测试未触发OOM的核心原因
你的代码和运行方式存在几个明显问题,根本没有复现对应测试场景:
- 第一处代码错误:你没有按要求移除外层的
runBlocking。runBlocking只会等待其协程作用域内的协程任务执行完成,你内部启动的是JVM原生平台线程,和runBlocking的协程上下文没有绑定关系。更关键的是,启动100万个线程的循环是单线程顺序执行的:创建一个平台线程本身需要微秒到毫秒级的开销,按每秒创建1万个线程的速度算,跑完100万次循环需要近2分钟,而你线程内写的sleep(5000L)只会让线程存活5秒,也就是说循环还没跑完1/10,最早启动的线程就已经执行完退出了,同一时间存活的线程数远达不到100万的量级,自然吃不到触发OOM的内存量。 - 第二处环境限制:普通消费级操作系统默认对单进程可创建的平台线程数有严格限制,Linux/macOS/Windows的默认阈值大多在几千到几万之间,根本不可能创建出100万个同时存活的平台线程。当循环创建线程数达到系统阈值时,会抛出
OutOfMemoryError: unable to create native thread错误,这个错误会直接中断跑循环的主线程,后续的线程创建逻辑直接终止,而已经创建的几千个线程会在sleep结束后打印.再退出。大量的打印输出很容易把前面的错误栈冲掉,再加上IDE控制台默认有输出行数限制,错误信息会被挤出可视区域,你肉眼很容易忽略掉已经抛出的错误。 - 第三点认知偏差:相关文档描述的核心是平台线程的内存开销远高于协程——单个JVM平台线程默认要占用1MB左右的栈内存,光是100万个线程的栈内存就需要近1TB,普通消费级硬件根本不可能支撑,而单个协程的内存开销只有几十字节,100万个协程只需要几十MB内存就能跑。你没看到OOM不代表线程和协程开销相当,只是你根本没造出100万个线程同时存活的场景而已。
正确的验证方式
如果要直观看到两者的差异,可以按下面的方式调整:
- 去掉外层的
runBlocking包装,main方法直接执行线程/协程创建逻辑 - 协程版本直接启动100万个协程执行delay,你会看到程序只占几十MB内存,几秒后就开始稳定打印
.,运行全程没有明显卡顿 - 线程版本不需要硬凑100万的量级,可以先从1万个线程开始测试,逐步往上加,你会发现加到几万线程的时候程序内存就会涨到几个G,系统开始明显卡顿,很快就会抛出无法创建线程的错误,和协程版本的开销差距非常明显
注意:如果你使用的是JDK 21+版本,要确认调用的
thread方法创建的是平台线程而非虚拟线程,虚拟线程的开销虽然比传统平台线程低很多,但和协程的调度模型、适用场景仍有差异,不要混为一谈。
内容的提问来源于stack exchange,提问作者user9628944
相关产品推荐
相关产品推荐

