具有数千个线程的内存设置

我正在Linux机箱(AMD 6 Core,16 GB RAM)上使用JVM(Oracle 1.7 64位)来查看应用程序中的线程数如何影响性能。 我希望测量上下文切换会降低性能。

我创建了一个创建线程执行池的小应用程序:

Executors.newFixedThreadPool(numThreads) 

我每次运行程序时都会调整numThreads ,以查看它的效果。

然后我将numThread作业( java.util.concurrent.Callable实例)提交到池中。 每一个都递增一个AtomicInteger ,做一些工作(创建一个随机整数数组并将其洗牌),然后睡一会儿。 想法是模拟Web服务调用。 最后,作业重新提交到池中,这样我总是可以使用numThreads作业。

我正在测量吞吐量,就像每分钟处理的作业数一样。

有几千个线程,我每分钟可以处理多达400,000个工作。 超过8000个线程,结果开始变化很大,这表明上下文切换正成为一个问题。 但是我可以继续将线程数增加到30,000,并且仍然可以获得更高的吞吐量(每分钟420,000到570,000个作业)。

现在的问题是:我得到了一个java.lang.OutOfMemoryError: Unable to create new native thread超过31,000个作业的java.lang.OutOfMemoryError: Unable to create new native thread 。 我试过设置-Xmx6000M这没有帮助。 我试过用-Xss玩,但这也无济于事。

我已经读过ulimit可能有用,但是增加ulimit -u 64000并没有改变任何东西。

有关信息:

 [root@apollo ant]# ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 127557 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 1024 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited 

所以问题#1:我需要做些什么才能创建更大的线程池?

问题2:我应该在什么阶段看到上下文切换确实降低了吞吐量并导致进程停止?


以下是一些结果,在我修改它以进行更多处理(如建议的那样)并开始记录平均响应时间(如同建议的那样)。

 // ( (n_cores x t_request) / (t_request - t_wait) ) + 1 // 300 ms wait, 10ms work, roughly 310ms per job => ideal response time, 310ms // ideal num threads = 1860 / 10 + 1 = 187 threads // // results: // // 100 => 19,000 thruput, 312ms response, cpu  28,500 thruput, 314ms response, cpu 50% // 180 => 34,000 thruput, 318ms response, cpu 60% // 190 => 35,800 thruput, 317ms response, cpu 65% // 200 => 37,800 thruput, 319ms response, cpu 70% // 230 => 42,900 thruput, 321ms response, cpu 80% // 270 => 50,000 thruput, 324ms response, cpu 80% // 350 => 64,000 thruput, 329ms response, cpu 90% // 400 => 72,000 thruput, 335ms response, cpu >90% // 500 => 87,500 thruput, 343ms response, cpu >95% // 700 => 100,000 thruput, 430ms response, cpu >99% // 1000 => 100,000 thruput, 600ms response, cpu >99% // 2000 => 105,000 thruput, 1100ms response, cpu >99% // 5000 => 131,000 thruput, 1600ms response, cpu >99% // 10000 => 131,000 thruput, 2700ms response, cpu >99%, 16GB Virtual size // 20000 => 140,000 thruput, 4000ms response, cpu >99%, 27GB Virtual size // 30000 => 133,000 thruput, 2800ms response, cpu >99%, 37GB Virtual size // 40000 => - thruput, -ms response, cpu >99%, >39GB Virtual size => java.lang.OutOfMemoryError: unable to create new native thread 

我将它们解释为:

1)即使应用程序在96.7%的时间内处于hibernate状态,仍然会留下大量的线程切换2)上下文切换是可测量的,并在响应时间中显示。

这里有趣的是,在调整应用程序时,您可能会选择可接受的响应时间,比如400毫秒,并增加线程数,直到您获得响应时间,在这种情况下,应用程序将处理大约95,000个请求分钟。

通常人们说理想的线程数接近核心数。 在具有等待时间的应用程序中(阻塞的线程,比如等待数据库或Web服务响应),计算需要考虑(参见上面的等式)。 但是,当您查看结果或调整到特定的响应时间时,即使理论上的理想也不是真正的理想。

我得到一个java.lang.OutOfMemoryError:无法创建超过31,000个作业的新本机线程。 我试过设置-Xmx6000M这没有帮助。 我试过用-Xss玩,但这也无济于事。

-Xmx设置无济于事,因为未从堆中分配线程堆栈。

发生的事情是,JVM要求操作系统提供一个内存段(堆外!)来保存堆栈,操作系统拒绝请求。 最可能的原因是ulimit或OS内存资源问题:

  • “数据段大小”ulimit,是无限的,所以这不应该是问题。

  • 这样就留下了内存资源。 每次1Mb的30,000个线程约为30Gb,这比你拥有的物理内存要多得多。 我的猜测是有足够的交换空间用于30Gb的虚拟内存,但是你已经将边界推得太远了。

-Xss设置应该有帮助,但是您需要使请求的堆栈大小小于默认大小1m 。 此外,还有一个很难的最小尺寸。

问题1:我需要做些什么才能创建更大的线程池?

将默认堆栈大小减小到当前值以下,或者增加可用虚拟内存量。 (不推荐使用后者,因为看起来你已经严重过度分配了。)

问题2:我应该在什么阶段看到上下文切换确实降低了吞吐量并导致进程停止?

无法预测到这一点。 它将高度依赖于线程实际执行的操作。 事实上,我认为您的基准测试不会给您答案,它将告诉您真正的multithreading应用程序将如何表现。


Oracle网站在线程堆栈空间的主题上说这个 :

在Java SE 6中,Sparc的默认值在32位VM中为512k,在64位VM中为1024k。 在x86 Solaris / Linux上,32位VM为320k,64位VM为1024k。

在Windows上,从二进制文件(java.exe)中读取默认的线程堆栈大小。 从Java SE 6开始,该值在32位VM中为320k,在64位VM中为1024k。

您可以使用-Xss选项运行来减少堆栈大小。 例如:

  java -server -Xss64k 

请注意,在某些版本的Windows上,操作系统可能会使用非常粗略的粒度来舍入线程堆栈大小。 如果请求的大小小于默认大小1K或更多,则堆栈大小向上舍入为默认值; 否则,堆栈大小向上舍入为1 MB的倍数。

64k是每个线程允许的最小堆栈空间量。

这里有一些要点/方法,我会遵循:

  1. 看一下上下文切换中使用的数据。 而不是布尔或字符串尝试使用一些大的List或Map。

  2. 而不是尝试在启动时创建固定池尝试使用缓存池。

  3. 在做一些小工作之后,不要让线程消失,让它们活着,然后一次又一次地做一小块工作。

  4. 尽量保持线程的处理时间更长。