目录
==========================【导读】[开始]==========================
工作中实践到了多线程与高并发应用,也踩了一些沉重的坑。
万丈高楼起于垒土,学习与总结+工作实践不可相离。
本篇主题“JVM 到底跑多少线程不会 OOM?”“线程池设置多少线程合适?”
==========================【导读】[结束]==========================
1 线程池设置多少线程合适?有哪些情况和因素需要考虑?
- 线程池过大,那么大量的线程将在相对很少的 CPU 和内存资源上发生竞争,这不仅会导致更高的内存使用量,而且还可能耗尽资源。
- 线程池过小,那么将导致许多空闲的处理器无法执行工作,从而降低吞吐率。
- 设置线程池的大小,必须分析计算环境、资源预算、任务的特性。在部署的系统中有多少个 CPU? 多大的内存? 任务是计算密集型、I/O密集型还是二者皆有?
- 计算密集型线程池大小设置参考:在 N 个处理器的系统上,当线程池的大小为 N+1 时,通常能实现最优的利用率。(即使当计算密集型的线程偶尔由于页缺失故障或者其他原因而暂停时,这个“额外”的线程也能确保CPU的时钟周期不会被浪费。)
- I/O密集型线程池大小设置参考:N(thread) = N(cpu)*(1+W/C) ,W:I/O 资源获取等待时间;C:除 I/O 资源获取外的计算时间。
- 当任务需要某种资源池来管理的资源时,例如数据库连接,那么线程池和资源池的大小将会相互影响,如果每个任务都需要一个数据库连接,那么连接池的大小就限制了线程池的大小。同样,当线程池中的任务是数据库连接的唯一使用者时,那么线程池的大小又将限制连接池的大小。
- 还需要考虑容器运行环境配置:tomcat 容器运行在 JVM 上,JVM 运行在 OS 上,JVM 的线程和OS 的线程一一对应。
2 JVM 到底跑多少线程不会 OOM?进行理论值分析探讨
- 理论值计算公式:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
MaxProcessMemory 指的是一个进程的最大内存
JVMMemory JVM 内存
ReservedOsMemory 保留的操作系统内存
ThreadStackSize 线程栈的大小
- Java 中,当你创建一个线程的时候,虚拟机会在 JVM 内存创建一个 Thread 对象同时创建一个操作系统线程,而这个系统线程的内存用的不是 JVMMemory,而是系统中剩下的内存(MaxProcessMemory - JVMMemory - ReservedOsMemory)。
- 由公式得出结论:给 JVM 内存越多,那么能创建的线程越少,越容易发生java.lang.OutOfMemoryError: unable to create new native thread。
- 如果程序确实需要大量的线程,现有的设置不能达到要求,可以通过修改 MaxProcessMemory(如使用64位操作系)、JVMMemory(如适当减少JVMMemory的分配)、ThreadStackSize(如适当减小单个线程的栈大小) 这三个因素,来增加能创建的线程数。这里只做理论分析。
- 用一个示例来计算一个理论值:
MaxProcessMemory 在 32 位的 windows 下是 2G;
JVMMemory eclipse 默认启动的程序内存是 64M;
ReservedOsMemory 一般是 130M 左右;
ThreadStackSize 32 位 JDK 1.6默认的是 325K 左右;
计算如下:
(2*1024*1024-64*1024-130*1024)/325 = 5841
3 小结
这里是纯理论分析,真实的项目环境,会更复杂,不会是只有一种类型的任务(单纯计算型或单纯IO 密集型)、项目处理的程序往往非常复杂,从理论分析也可以给我探究线程池线程设置做一些参考。