一台后端server,OS为Slackware 8.1,装了tomcat 4.1.30,近期在繁忙时期经常会死机,死状就是"java.lang.OutOfMemoryError: unable to create new native thread"。是tomcat创建不了新的线程来应答请求了。于是我搭了一个环境专门来测试这个问题。内存为2G,CPU为四颗2.8G,tomcat 4.1.30,写一个最简单的JSP页面,如下:
Java代码
- <%
- try
- {
- Thread.sleep(30000);;
- out.println("fuck");;
- } catch (InterruptedException e); {
- e.printStackTrace();;
- }
- %>
-
-
- 然后开Jmetor来压,同时开jconsole来监测tomcat的情况,并不断调整XMX,XMS,XSS这三个参数,得出下表:
-
- XMX XMS XSS down时的tomcat thread数
- 500M 500M 128K 642
- 800M 800M 64K 485
- 1024M 1024M 64K 374
- 1024M 1024M 128K 374
- 1024M 1024M 512K 371
-
- 根据该表,可以看出,随XMX,即是分配给JVM的内存数越大,tomcat所能开的thread数就越小,而Xss这个参数几乎不影响任何测试结果。我猜想tomcat开线程是使用linux的内存,而不是JVM的内存。当分配给JVM的内存越大,操作系统所能用于分配的内存就越小,于是所能开的线程数就越小。
-
- 大家有什么解决方案吗?难道大家都没遇过这个问题?我的设想是可能linux初始每个进程(也就是tomcat开的线程)有一个初始大小,这应该是一个内核参数来的,应该把它调小就可以了,但我不知道怎样去调。
在这里找到了一篇文章,好像说的和我的情况一模一样。
引用
Java Performance Part III - When 4294967296 bytes of address space isn't enough
I've done some interesting work lately, improving performance on a server designed to handle thousands of stateful concurrent connections. One of the major problems I've been seeing is the mass proliferation of threads. I was seeing memory errors like:
java.lang.OutOfMemoryError: unable to create new native thread
The problem with this message is that the server actually had plenty of memory to go around. The max heap was set to 1G, but it was only using up perhaps a few hundred megabytes of that. Initially, I thought that, perhaps, the memory being allocated for the thread's stack wasn't being reported against the heap and was causing these out of memory errors. I remembered reading somewhere that the default Window's thread stack size was 1M. According to MSDN, what really happens, is that the operating system will reserve a certain amount of address space for each thread, but only commit a percentage of it to physical memory. The actual amount of memory committed and address space reserved isn't documented there, though it does note that if you change the initial amount committed to be larger than the default, the operating system will reserve the same amount of address space, rounded up to the nearest megabyte - hence using -Xss to change the initial stack size (even just slightly) can cause a much larger amount of address space to be reserved - potentially an entire megabyte.
After running some tests with the JVM, I've come to the conclusion that Sun's JDK 1.4 allocates about 256K of address space per thread. I also ran some tests with JDK 1.5, which seem to indicate that it was allocating about 1M of address space per thread. Those numbers come from an assumption of a 2G address space per process under Windows, and the following statistics for the maximum number of threads I could create:
JDK1.4
-Xmx750 = 4580 threads.
-Xmx1000 = 3608 threads.
-Xmx1500M = 1663 threads
JDK1.5
-Xmx750M = 1129 threads
-Xmx1000M = 880 threads
-Xmx1500M = 384 threads
As you can see, the maximum number of threads I can create decreases as the heap size gets larger. This is because the JVM immediately reserves the address space specified for the maximum heap size - That's probably because it needs that memory to be contiguous.
This tells us two things:
1) There are very finite limits on the maximum number of threads that you can create in an application, and those numbers are very small. This only reinforces the mantra that server's should be designed to operate with a fairly small and constant number of threads.
2) If you're really stuck creating a lot of threads, you can maximize the number available to you by reducing the address space allocated by the VM through a lower maximum heap size. You should also avoid changing the thread stack size, as a larger stack size can result in a much larger consumption of address space. Finally, unless things change, you should be prepared to run under JDK1.4 for a long time.