JVM棧以及其對(duì)線程并發(fā)量的影響

JVM棧

?根據(jù)JVM規(guī)范,JVM包括兩種棧,java虛擬機(jī)棧和本地方法棧。也就是說(shuō),每當(dāng)啟動(dòng)一個(gè)java線程時(shí),JVM就會(huì)為其分配一個(gè)java虛擬機(jī)棧和一個(gè)本地方法棧。棧對(duì)線程而言是獨(dú)立私有內(nèi)存區(qū)域,線程間無(wú)法對(duì)棧進(jìn)行互訪,這由虛擬機(jī)保證。如下圖:

?

?java虛擬機(jī)棧在老的規(guī)范中描述為java棧,顧名思義,是為java方法即java 字節(jié)碼方法調(diào)用服務(wù)的棧。如同C棧,每當(dāng)java線程調(diào)用java方法時(shí),JVM會(huì)向java虛擬機(jī)棧壓入新的棧幀,該棧幀成為當(dāng)前幀,用來(lái)存儲(chǔ)局部變量,中間運(yùn)算值等數(shù)據(jù)。

?本地方法棧是為本地方法調(diào)用服務(wù)的棧,如通過(guò)JNI、JNA接口實(shí)現(xiàn)的本地方法調(diào)用,方法棧具體是什么類型由本地方法接口的實(shí)現(xiàn)決定,如其采用C鏈接模型,那么本地方法棧就是C棧。JVM規(guī)范并沒(méi)有對(duì)本地方法棧使用的語(yǔ)言、數(shù)據(jù)結(jié)構(gòu)進(jìn)行強(qiáng)制規(guī)定,由具體虛擬機(jī)實(shí)現(xiàn)決定。當(dāng)線程調(diào)用本地方法時(shí),java虛擬機(jī)棧并不會(huì)改變,JVM只是動(dòng)態(tài)鏈接和調(diào)用本地方法,由本地方法棧完成本地方法的壓棧出棧。

JVM棧對(duì)線程并發(fā)量的影響

?由上可知,JVM會(huì)為每個(gè)線程分配虛擬機(jī)棧和本地方法棧,但受資源的約束線程數(shù)是無(wú)法無(wú)限增長(zhǎng)的,這除了操作系統(tǒng)設(shè)置的影響外,JVM棧大小對(duì)JVM能創(chuàng)建的線程數(shù)也有間接影響。

? 操作系統(tǒng)為每個(gè)進(jìn)程分配的內(nèi)存是有限的,在粗略計(jì)算下,拋開其它小的消耗,分配給JVM進(jìn)程的內(nèi)存減去JVM堆、方法區(qū)、基本就是棧區(qū)所能使用的最大內(nèi)存了。如win32下,一個(gè)JVM最大內(nèi)存是2G,如果分配給堆和方法區(qū)1G,剩下1G是棧區(qū)所能使用的內(nèi)存,每創(chuàng)建一個(gè)線程就需要分配一定的棧內(nèi)存給線程,線程數(shù)必然可盡。

?創(chuàng)建新線程無(wú)法申請(qǐng)到足夠棧內(nèi)存,容易出現(xiàn)內(nèi)存溢出錯(cuò)誤,或者JVM崩潰。這常出現(xiàn)在小內(nèi)存,大并發(fā)線程量的應(yīng)用中。如下的虛擬機(jī)崩潰問(wèn)題也許似曾相識(shí):

#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 133419008 bytes for committing reserved memory.
# Possible reasons:
#   The system is out of physical RAM or swap space
#   In 32 bit mode, the process size limit was hit
# Possible solutions:
#   Reduce memory load on the system
#   Increase physical memory or swap space
#   Check if swap backing store is full
#   Use 64 bit Java on a 64 bit OS
#   Decrease Java heap size (-Xmx/-Xms)
#   Decrease number of Java threads
#   Decrease Java thread stack sizes (-Xss)
#   Set larger code cache with -XX:ReservedCodeCacheSize=
# This output file may be truncated or incomplete.
#

這樣的應(yīng)用很常見(jiàn),尤其是把服務(wù)架設(shè)在云服務(wù)器上用幾十塊錢一個(gè)月的機(jī)器當(dāng)服務(wù)器的初創(chuàng)團(tuán)隊(duì)。

?除了可以在服務(wù)器資源投入上解決問(wèn)題外,依然可以在現(xiàn)有的有限條件下解決問(wèn)題:一是降低其他內(nèi)存區(qū)的內(nèi)存占用量,如堆區(qū),二是采用workaround,任務(wù)調(diào)度方案,減少并發(fā)執(zhí)行量,另一個(gè)很少受到關(guān)注的方法是調(diào)整java線程棧的大小。

?-Xss可用來(lái)設(shè)置JVM單個(gè)線程的棧大小,該參數(shù)不帶單位時(shí)指字節(jié),后面可以帶k或K,m或M,g或G等單位,分別表示KB、MB、GB。JDK5以后每個(gè)java線程的棧內(nèi)存默認(rèn)是1024KB,即1M,1G的JVM棧最多支持1024個(gè)線程。如果應(yīng)用中并無(wú)很深的調(diào)用,根據(jù)應(yīng)用的特點(diǎn)降低JVM棧大小也是能達(dá)到較大的線程并發(fā)量,如-Xss256m。這樣1G的棧內(nèi)存可支持4*1024個(gè)線程。另外,需要補(bǔ)充說(shuō)明的是,Hotspot做為主流JVM,其實(shí)現(xiàn)是將java虛擬機(jī)棧和本地方法棧合二為一,因此,如Xoss針對(duì)本地方法區(qū)大小設(shè)置是無(wú)效的。

參考

? 深入理解Java虛擬機(jī) 周志明

? 深入Java虛擬機(jī) Bill Venners

? Java虛擬機(jī)規(guī)范8 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.6

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容