Java OutOfMemory Error引发的JVM参数实测--线程堆栈参数篇

来源:互联网 发布:js apply的z作用 编辑:程序博客网 时间:2024/06/15 08:54

最近在Tomcat的环境中调整了JVM的线程堆栈参数Xss,结果服务器运行一段时间后出现OutOfMemory的错误,导致Tomcat进程异常挂起。于是乎在网上找了大量的资料,并在环境中实际进行测试,终于搞清了这个问题。下面将详细的信息和过程罗列如下,供大家查看和参考。在这篇文章中会重点测试不同的Xss参数对生成的线程数量的影响。以后会再写一篇文章重点测试不同的Xms和Xmx参数对生成的线程数量的影响。得意

先说一下我遇到问题的环境信息。服务器操作系统为Linux RHEL 32位,Tomcat配置的Java为Java 1.6.0_20 32位。以前采用的默认的Xss参数设置,服务器运行正常。从网上看到一些文章说Java 1.5之后的版本的默认线程堆栈大小为1M,于是就把我的环境中的Xss设置为512K,这样就可以生成更多的Java线程,提高Tomcat的处理能力。结果调整完之后就出现Tomcat运行一段时间后异常挂起的情况抓狂。以下是JVM的报错信息:

# java.lang.OutOfMemoryError: requested 1059528 bytes for Chunk::new. Out of swap space?
#
#  Internal Error (allocation.cpp:215), pid=11588, tid=212859792
#  Error: Chunk::new
#
# JRE version: 6.0_20-b02
# Java VM: Java HotSpot(TM) Server VM (16.3-b01 mixed mode linux-x86 )

开始以为是Tomcat的线程池分配过大引起过量的内存消耗引起的,但是将线程池缩小后仍然出现同样的问题。遂开始怀疑是不是Xss的调整引起的问题,如果我的环境中Java版本的默认线程堆栈不是1M,而是小于512K的某个值,那么当我把Xss设置为512K后,则同样的线程数量会消耗更多的内存,从而引起内存溢出的问题。于是在网上开搜,找了很多资料,终于在Oracle的网站上发现这么一段话:( http://www.oracle.com/technetwork/java/hotspotfaq-138619.html )

------------------------------------------------------------------------------------------------------

My application has a lot of threads and is running out of memory, why?

You may be running into a problem with the default stack size for threads. In Java SE 6, the default on Sparc is 512k in the 32-bit VM, and 1024k in the 64-bit VM. On x86 Solaris/Linux it is 320k in the 32-bit VM and 1024k in the 64-bit VM.

On Windows, the default thread stack size is read from the binary (java.exe). As of Java SE 6, this value is 320k in the 32-bit VM and 1024k in the 64-bit VM.

You can reduce your stack size by running with the -Xss option. For example:

java -server -Xss64k
Note that on some versions of Windows, the OS may round up thread stack sizes using very coarse granularity. If the requested size is less than the default size by 1K or more, the stack size is rounded up to the default; otherwise, the stack size is rounded up to a multiple of 1 MB. 64k is the least amount of stack space allowed per thread.

----------------------------------------------------------------------------------------------------------

果然如我的猜测,Java 1.6 32bit的默认线程堆栈大小是320k,64bit才是1M。之前受了网上某些文章的误导,才导致了上述的错误。

在搜索资料的过程中,发现了某位网友提供的一段code用来测试Java可以生成的线程数量。为了进一步验证,用下面这段code在环境中进行了实际测试。测试过程及结论如下:


测试代码:

  1. import java.util.concurrent.CountDownLatch;  
  2.   
  3. public class TestNativeOutOfMemoryError {  
  4.   
  5.     public static void main(String[] args) {  
  6.   
  7.         for (int i = 0;; i++) {  
  8.             System.out.println("i = " + i);  
  9.             new Thread(new HoldThread()).start();  
  10.         }  
  11.     }  
  12.   
  13. }  
  14.   
  15. class HoldThread extends Thread {  
  16.     CountDownLatch cdl = new CountDownLatch(1);  
  17.   
  18.     public HoldThread() {  
  19.         this.setDaemon(true);  
  20.     }  
  21.   
  22.     public void run() {  
  23.         try {  
  24.             cdl.await();  
  25.         } catch (InterruptedException e) {  
  26.         }  
  27.     }  

测试目的

通过Sun  Java 1.6.0_20 32bit 版本运行上述的程序,测试在不同的线程堆栈参数设置的情况下分别可以生成多少个线程。

测试一:

使用默认的Java设置,测试生成的Java线程数:

./jre1.6.0_20/bin/java -server TestNativeOutOfMemoryError

..........................

i = 9139
i = 9140
i = 9141
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
        at java.lang.Thread.start0(Native Method)
        at java.lang.Thread.start(Unknown Source)
        at TestNativeOutOfMemoryError.main(TestNativeOutOfMemoryError.java:9)
Java HotSpot(TM) Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal SIGINT to handler- the VM may need to be forcibly terminated


运行java 程序之前,系统可用内存:

[root@tivbsmv999 ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          3949       2742       1206          0        154       1928
-/+ buffers/cache:        659       3289
Swap:         5951         10       5941

运行java 程序之后,系统可用内存:

[root@tivbsmv999 ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          3949       3120        829          0        154       1928
-/+ buffers/cache:       1036       2912
Swap:         5951         10       5941


测试二:

将线程堆栈大小设置为256k,测试是否会生成更多的线程:

./jre1.6.0_20/bin/java -server -Xss256k TestNativeOutOfMemoryError
.................................

i = 11361
i = 11362
i = 11363
i = 11364
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
        at java.lang.Thread.start0(Native Method)
        at java.lang.Thread.start(Unknown Source)
        at TestNativeOutOfMemoryError.main(TestNativeOutOfMemoryError.java:9)


运行Java程序之前,系统的可用内存:

[root@tivbsmv999 ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          3949       2741       1208          0        154       1928
-/+ buffers/cache:        657       3291
Swap:         5951         10       5941


运行Java程序之后,系统的可用内存:
[root@tivbsmv999 ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          3949       3198        750          0        154       1928
-/+ buffers/cache:       1115       2834
Swap:         5951         10       5941


测试三:

将线程堆栈大小设置为512k,测试可以生成多少个线程:

./jre1.6.0_20/bin/java -server -Xss512k TestNativeOutOfMemoryError

..........................................

i = 5777
i = 5778
i = 5779
i = 5780
i = 5781
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
        at java.lang.Thread.start0(Native Method)
        at java.lang.Thread.start(Unknown Source)
        at TestNativeOutOfMemoryError.main(TestNativeOutOfMemoryError.java:9)


运行Java程序之前,系统的可用内存:
[root@tivbsmv999 ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          3949       2741       1208          0        155       1928
-/+ buffers/cache:        658       3291
Swap:         5951         10       5941


运行Java程序之后,系统的可用内存:
[root@tivbsmv999 ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          3949       2985        964          0        155       1928
-/+ buffers/cache:        901       3048
Swap:         5951         10       5941

同样的JVM参数配置下,线程堆栈大小Xss=320k时,其生成的线程数量应该是线程堆栈大小Xss=512k时生成线程数量的1.6倍(512 / 320 = 1.6)。

将上述测试三生成的线程对比测试一生成的线程数量,9141 / 5178  = 1.58,结果基本吻合。


测试四:

这次测试中,将线程堆栈大小Xss显式地设置为320k,看一下测试结果是否与测试场景一的结果相似:

 ./jre1.6.0_20/bin/java -server-Xss320k TestNativeOutOfMemoryError

.................................

i = 9141
i = 9142
i = 9143
i = 9144
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
        at java.lang.Thread.start0(Native Method)
        at java.lang.Thread.start(Unknown Source)
        at TestNativeOutOfMemoryError.main(TestNativeOutOfMemoryError.java:9)


运行Java程序之前,系统的可用内存:

[root@tivbsmv999 ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          3949       2742       1206          0        155       1928
-/+ buffers/cache:        658       3291
Swap:         5951         10       5941


运行Java程序之后,系统的可用内存:
[root@tivbsmv999 ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          3949       3121        827          0        155       1928
-/+ buffers/cache:       1036       2912
Swap:         5951         10       5941

测试结果分析:

1. 从上述的测试结果可以验证,Java 1.6 32bit的默认线程堆栈大小为320k;

2. 通过Xss参数调整线程的堆栈大小后,生成的线程数量会相应的成比例增加或减少;

3. 不同的堆栈参数情况下,出现Out of Memory时所消耗的内存数量也不一样;

原创粉丝点击