工作中碰到Java程序报错,无法创建本地线程。在这里总结一下。
思路1, 通过ulimit指令限制 shell 多线程程序堆栈的大小,达到增加可用线程数量的目的
个例子取自于一个真实的案例。我们所遇到的问题是系统对我们的多线程程序有如下的限制:
ulimit -v 200000
根据本文前面的介绍,这意味着我们的程序最多只能使用不到 200MB 的虚拟内存。由于我们的程序是一个多线程程序,程序在运行时会根据需要创建新的线程,这势必会增加总的内存需求量。一开始我们对堆栈大小的限制是 1024 (本例子中使用 1232 来说明):
# ulimit – s 1232 |
当我们的程序启动后,通过 pmap 来查看其内存使用情况,可以看到多个占用 1232KB 的数据段,这些就是程序所创建的线程所使用的堆栈:
每当一个新的线程被创建时都需要新分配一段大小为 1232KB 的内存空间,而我们总的虚拟内存限制是 200MB,所以如果我们需要创建更多的线程,那么一个可以改进的方法就是减少每个线程的固定堆栈大小,这可以通过 ulimit – s 来实现:
# ulimit -s 512 |
我们将堆栈大小设置为 512KB,这时再通过 pmap 查看一下我们的设置是否起作用:
从上面的信息可以看出,我们已经成功的将线程的堆栈大小改为 512KB 了,这样在总内存使用限制不变的情况下,我们可以通过本小节介绍的方法来增加可以创建的线程数,从而达到改善程序的多线程性能。
思路2,从JVM着手进行
jvm启动参数之Xss
-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M。在sunos下32位java测试这值为512K。可以根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
"Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread"异常问题本质原因是程序创建了太多的线程,而能创建的线程数是有限制的,导致了异常的发生。
能创建的线程数的具体计算公式如下:
(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。
--根据测试,这种说法只适用于32位java程序,64位的java不受此限制
测试程序
import java.util.concurrent.CountDownLatch; public class TestNativeOutOfMemoryError { public static void main(String[] args) { for (int i = 0;; i++) { System.out.println("i = " + i); new Thread(new HoldThread()).start(); } } } class HoldThread extends Thread { CountDownLatch cdl = new CountDownLatch(1); public HoldThread() { this.setDaemon(true); } public void run() { try {cdl.await();} catch (InterruptedException e) {} } }
如果程序确实需要大量的线程,现有的设置不能达到要求,那么可以通过修改MaxProcessMemory,JVMMemory,ThreadStackSize这三个因素,来增加能创建的线程数:
a, MaxProcessMemory 使用64位JVM
经测试在,64位jvm下生成的线程数不受上述公式的制约,值为63260,这个数值应该足够用了,
测试数据如下, (指令|能启动的线程数量)
java -Xms512M -Xmx2G -cp . TestNativeOutOfMemoryError | 63389 java -Xms512M -Xmx8G -cp . TestNativeOutOfMemoryError | 63260 java -Xms512M -Xmx16G -cp . TestNativeOutOfMemoryError | 63385 java -Xms512M -Xmx32G -cp . TestNativeOutOfMemoryError | 63380 java -Xms512M -Xmx64G -cp . TestNativeOutOfMemoryError |63387
b, JVMMemory 减少JVMMemory的分配(即减少xms/xmx的大小)
c, ThreadStackSize 减小单个线程的栈大小 (-Xss)