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

Ant*_*era 11 java memory multithreading

我玩弄的JVM(甲骨文1.7 64位)在Linux机器上(AMD 6核心,16 GB RAM)看到线程的应用程序的数量如何影响系统性能.我希望测量上下文切换会降低性能.

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

Executors.newFixedThreadPool(numThreads)
Run Code Online (Sandbox Code Playgroud)

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个工作岗位.我试过设置-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
Run Code Online (Sandbox Code Playgroud)

所以问题#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 < 50%
//   150 =>  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
Run Code Online (Sandbox Code Playgroud)

我将它们解释为:

1)即使在应用程序睡时96.7%,这仍然留下大量的线程切换做2)上下文切换是可测量的,并且在响应时间被示出.

这里有趣的是,当调整一个应用程序,你可以选择在可接受的响应时间,400毫秒说,增加线程数,直到你得到的响应时间,在这种情况下,将让应用程序围绕95000请求分钟.

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

Ste*_*n C 7

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

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

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

  • "数据段大小"ulimit,是无限的,所以不应该是问题.

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

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

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

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

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

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


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
Run Code Online (Sandbox Code Playgroud)

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

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


Pra*_*eek 2

我会遵循以下一些要点/方法:

  1. 看一下上下文切换中使用的数据。尝试使用一些大列表或映射,而不是布尔值或字符串。

  2. 不要尝试在开始时创建固定池,而是尝试使用缓存池。

  3. 不要让线程在完成一些小工作后就消失,而是让它们保持活动状态并一次又一次地回来完成小块工作。

  4. 尝试保持线程的处理时间更长。