星期一早上到了公司,據(jù)稱產(chǎn)品環(huán)境拋出了最可愛(ài)的異常—OutOfMemory, 它是這樣來(lái)描述他自己的:
java.lang.OutOfMemoryError: unable to create new native thread
而且這位仁兄竟然還堂而皇之地同時(shí)出現(xiàn)在了3個(gè)application里面,所有應(yīng)用全部遭殃。
那可愛(ài)的OOM是如何產(chǎn)生的呢?直接原因是創(chuàng)建的線程太多了,根本原因是某個(gè)地方的內(nèi)存限制了。
搜羅了一下在網(wǎng)上找到了一個(gè)計(jì)算公式:
(MaxProcessMemory - JVMMemory – ReservedOsMemory) / (ThreadStackSize) = Number of threads
MaxProcessMemory:進(jìn)程最大的尋址空間,但我想這個(gè)值應(yīng)該也不會(huì)超過(guò)虛擬內(nèi)存和物理內(nèi)存的總和吧。關(guān)于不同系統(tǒng)的進(jìn)程可尋址的最大空間,可參考下面表格:
Maximum Address Space Per Process
|
Operating System
|
Maximum Address Space Per Process
|
Redhat Linux 32 bit
|
2 GB
|
Redhat Linux 64 bit
|
3 GB
|
Windows 98/2000/NT/Me/XP
|
2 GB
|
Solaris x86 (32 bit)
|
4 GB
|
Solaris 32 bit
|
4 GB
|
Solaris 64 bit
|
Terabytes
|
JVMMemory: Heap + PermGen
ReservedOSMemory:Native heap,JNI
便可推導(dǎo)出單個(gè)JVM Instance可支持的最大線程數(shù)的估計(jì)值:
(MaxProcessMemory<固定值> – Xms<初始化值,最小值> – XX:PermSize<初始化值,最小值> – 100m<估算值>) / Xss = Number of threads<最大值>
在本地(32bit windows)試了試,可達(dá)的線程的最大值差不多就是這個(gè)數(shù),它不受物理內(nèi)存的限制,會(huì)利用虛擬內(nèi)存,從任務(wù)管理器看到memory已經(jīng)是5500 m左右了(開(kāi)了兩個(gè)jvm),我機(jī)器的物理內(nèi)存是2g,也不知道這個(gè)準(zhǔn)不準(zhǔn),后來(lái)還拋出了“unable to create new native thread”的兄弟“Exception in thread "CompilerThread0" java.lang.OutOfMemoryError: requested 471336 bytes for Chunk::new. Out of swap space?“。
本地測(cè)完了后,就該輪到dev環(huán)境了,linux2.6,64bit,雙核,8G(虛擬機(jī)),總的物理內(nèi)存是16g。在上面整了一下,創(chuàng)建到了15000多個(gè)線程的時(shí)候掛掉了。此時(shí)其他application也不能創(chuàng)建新的線程,而且db也報(bào)錯(cuò)了,操作系統(tǒng)不能fork新的線程了。這應(yīng)該是操作系統(tǒng)的哪里限制了新線程的創(chuàng)建,
· max thread,linux2.6似乎是32000
· 最大可用內(nèi)存:物理內(nèi)存+虛擬內(nèi)存
· 配置,在linux可以限制可用資源的大小,show一下這些參數(shù)
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
pending signals (-i) 1024
max locked memory (kbytes, -l) 32
max memory size (kbytes, -m) unlimited
open files (-n) 65536
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 16384
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
|
為了進(jìn)一步確定在linux上一個(gè)jvm因?yàn)檫_(dá)到了最大尋址空間OOM了,不會(huì)影響其他jvm,我在Linux做了進(jìn)一步測(cè)試,一開(kāi)始用Sun文檔中說(shuō)的最大尋址空間3G試了一下,發(fā)現(xiàn)根本不對(duì),達(dá)到了3G后還是非常high地在創(chuàng)建新的線程。于是出動(dòng)超級(jí)無(wú)敵變態(tài)的JVM初始化配置。
oracle 27408 27017 12 13:45 ? 00:00:07 /home/oracle/ias1013/FWAPP/FWDev/jdk/bin/java -server -Xmx4096m -Xms4096m -XX:+HeapDumpOnOutOfMemoryError -XX:PermSize=4096m -XX:MaxPermSize=4096m -XX:HeapDumpPath=/home/oracle/ias1013/FWAPP/FWDev/j2ee/OC4J_OOMTest/workEnv/log -Xss100m
|
結(jié)果在create 3379個(gè)線程后,“unable to create new native thread”出現(xiàn)了,這時(shí)其他jvm都是可以create新線程的。如果按照上面公式計(jì)算,linux 64bit,2.6kernel,它的最大尋址空間肯定超過(guò)了300g,當(dāng)然應(yīng)該還沒(méi)有達(dá)到可用內(nèi)存的限制,因?yàn)槠渌?/span>JVM還能create新線程。
我還懷疑是不是oracle application server上的某個(gè)配置參數(shù)限制了總的線程數(shù),影響了所有application,但我們的產(chǎn)品環(huán)境一個(gè)application就是一個(gè)單獨(dú)的application server。
現(xiàn)在基本上可以確定是操作系統(tǒng)哪里設(shè)置錯(cuò)了,我想System team的帥哥們應(yīng)該把產(chǎn)品環(huán)境的某個(gè)參數(shù)配置錯(cuò)了,系統(tǒng)本身的影響肯定不會(huì)有了,因?yàn)楫a(chǎn)品環(huán)境上我們只create了800左右個(gè)線程,就OOM了,那應(yīng)該就是配置的問(wèn)題了,懷疑的參數(shù)有下面四個(gè)
max user processes (-u) 2048
virtual memory (kbytes, -v) unlimited
max memory size (kbytes, -m) unlimited
stack size (kbytes, -s) 10240
最后發(fā)現(xiàn)只有max user processes 和virtual memory對(duì)總的線程數(shù)有影響,我把max user processes降到2048后,發(fā)現(xiàn)此時(shí)只能創(chuàng)建 2000左右個(gè)線程了(Xms64m, Xss1m),進(jìn)一步地把virtual memory下調(diào)到2048000K發(fā)現(xiàn)能創(chuàng)建的就更少了1679(Xms64m, Xss1m),而它只會(huì)對(duì)當(dāng)前shell起作用,而多個(gè)application server應(yīng)該是不同的shell,所以他是打醬油的。另外兩個(gè)參數(shù)好像就是來(lái)做做俯臥撐的,操作系統(tǒng)stack size是不應(yīng)該會(huì)有什么影響,我們把它上調(diào)到102400,還是可以創(chuàng)建2000左右的線程數(shù)(max user processes),因?yàn)?/span>java有自己的線程模型,它的棧的大小是用Xss來(lái)控制的。Max memory size不知道是啥東東,照理說(shuō)如果是最大內(nèi)存應(yīng)該不會(huì)只在旁邊做俯臥撐,那這個(gè)參數(shù)到底是春哥還是曾哥,查了一下man ulimit,有下面解釋
-a All current limits are reported
-c The maximum size of core files created
-d The maximum size of a process data segment
-f The maximum size of files created by the shell
-l The maximum size that may be locked into memory
-m The maximum resident set size (has no effect on Linux)
-n The maximum number of open file descriptors (most systems do not allow this value to be set)
-p The pipe size in 512-byte blocks (this may not be set)
-s The maximum stack size
-t The maximum amount of cpu time in seconds
-u The maximum number of processes available to a single user
-v The maximum amount of virtual memory available to the shell
“Has no effect on Linux”就足以證明它確實(shí)只是來(lái)做做俯臥撐的。最后查出只有“max user processes”會(huì)對(duì)所有application能創(chuàng)建總的線程數(shù)有限制。