大家好,我是小林。前幾天基金還大漲了一波,結(jié)果今天一早上打開支付寶,發(fā)現(xiàn)昨天的基金收益直接 -3000多。。。你說不心疼是假的,但是我短期內(nèi)也不會賣出,所以在我不賣出的時候,它就是個數(shù)字,而且現(xiàn)在總體收益還是正數(shù)的,不慌!老讀者應該知道,我是今年年初入坑基金的
韭零后,選了幾個基金后,就一直無腦定投,風雨無阻。在 3 月份的時候,總體收益最低的時候是 -10000,不過由于一直在持續(xù)定投,在 5 月份收益已經(jīng)從負數(shù)變成正數(shù)了,收益也不算多,就小幾千。這玩意漲跌每天都在發(fā)生,我就偶爾打開看看收益的情況,每次打開驚喜筆比較少,驚嚇到是比較多些。好了,基金的事情就說到這了,我也算個反面教材,大家看個樂就好。身為技術(shù)博主,那肯定還是得說說技術(shù)的事情。昨天有位讀者
被坑了,問了我這么個問題:大致意思就是,他看了一個面經(jīng),說虛擬內(nèi)存是 2G 大小,然后他看了我的圖解系統(tǒng) PDF 里說虛擬內(nèi)存是 4G,然后他就懵逼了。其實他看這個面經(jīng)很有問題,沒有說明是什么操作系統(tǒng),以及是多少位操作系統(tǒng)。因為不同的操作系統(tǒng)和不同位數(shù)的操作系統(tǒng),虛擬內(nèi)存可能是不一樣多。Windows 系統(tǒng)我不了解,我就說說 Linux 系統(tǒng)。?在 Linux 操作系統(tǒng)中,虛擬地址空間的內(nèi)部又被分為內(nèi)核空間和用戶空間兩部分,不同位數(shù)的系統(tǒng),地址 空間的范圍也不同。比如最常?的 32 位和 64 位系統(tǒng),如下所示:通過這里可以看出:
- 32 位系統(tǒng)的內(nèi)核空間占用 1G ,位于最高處,剩下的 3G 是用戶空間;
- 64 位系統(tǒng)的內(nèi)核空間和用戶空間都是 128T ,分別占據(jù)整個內(nèi)存空間的最高和最低處,剩下的中
間部分是未定義的。
接著,來看看讀者那個面經(jīng)題目:
一個進程最多可以創(chuàng)建多少個線程?這個問題跟兩個東西有關(guān)系:
- 進程的虛擬內(nèi)存空間上限,因為創(chuàng)建一個線程,操作系統(tǒng)需要為其分配一個??臻g,如果線程數(shù)量越多,所需的??臻g就要越大,那么虛擬內(nèi)存就會占用的越多。
- 系統(tǒng)參數(shù)限制,雖然 Linux 并沒有內(nèi)核參數(shù)來控制單個進程創(chuàng)建的最大線程個數(shù),但是有系統(tǒng)級別的參數(shù)來控制整個系統(tǒng)的最大線程個數(shù)。
我們先看看,在進程里創(chuàng)建一個線程需要消耗多少虛擬內(nèi)存大?。?/p>我們可以執(zhí)行 ulimit -a 這條命令,查看進程創(chuàng)建線程時默認分配的??臻g大小,比如我這臺服務器默認分配給線程的棧空間大小為 8M。在前面我們知道,在 32 位 Linux 系統(tǒng)里,一個進程的虛擬空間是 4G,內(nèi)核分走了1G,
留給用戶用的只有 3G。那么假設(shè)創(chuàng)建一個線程需要占用 10M 虛擬內(nèi)存,總共有 3G 虛擬內(nèi)存可以使用。于是我們可以算出,最多可以創(chuàng)建差不多 300 個(3G/10M)左右的線程。如果你想自己做個實驗,你可以找臺 32 位的 Linux 系統(tǒng)運行下面這個程序:由于我手上沒有 32 位的系統(tǒng),我這里貼一個網(wǎng)上別人做的測試結(jié)果:如果想使得進程創(chuàng)建上千個線程,那么我們可以調(diào)整創(chuàng)建線程時分配的棧空間大小,比如調(diào)整為 512k:
$?ulimit?-s?512
說完 32 位系統(tǒng)的情況,我們來看看 64 位系統(tǒng)里,一個進程能創(chuàng)建多少線程呢?我的測試服務器的配置:
- 64 位系統(tǒng);
- 2G 物理內(nèi)存;
- 單核 CPU。
64 位系統(tǒng)意味著用戶空間的虛擬內(nèi)存最大值是 128T,這個數(shù)值是很大的,如果按創(chuàng)建一個線程需占用 10M 棧空間的情況來算,那么理論上可以創(chuàng)建 128T/10M 個線程,也就是 1000多萬個線程,有點魔幻!所以按 64 位系統(tǒng)的虛擬內(nèi)存大小,理論上可以創(chuàng)建無數(shù)個線程。事實上,肯定創(chuàng)建不了那么多線程,除了虛擬內(nèi)存的限制,還有系統(tǒng)的限制。比如下面這三個內(nèi)核參數(shù)的大小,都會影響創(chuàng)建線程的上限:
- /proc/sys/kernel/threads-max,表示系統(tǒng)支持的最大線程數(shù),默認值是?
14553
; - /proc/sys/kernel/pid_max,表示系統(tǒng)全局的 PID 號數(shù)值的限制,每一個進程或線程都有 ID,ID 的值超過這個數(shù),進程或線程就會創(chuàng)建失敗,默認值是?
32768
; - /proc/sys/vm/max_map_count,表示限制一個進程可以擁有的VMA(虛擬內(nèi)存區(qū)域)的數(shù)量,具體什么意思我也沒搞清楚,反正如果它的值很小,也會導致創(chuàng)建線程失敗,默認值是?
65530
。
那接下針對我的測試服務器的配置,看下一個進程最多能創(chuàng)建多少個線程呢?我在這臺服務器跑了前面的程序,其結(jié)果如下:可以看到,創(chuàng)建了 14374 個線程后,就無法在創(chuàng)建了,而且報錯是因為資源的限制。前面我提到的?
threads-max
?內(nèi)核參數(shù),它是限制系統(tǒng)里最大線程數(shù),默認值是 14553。我們可以運行那個測試線程數(shù)的程序后,看下當前系統(tǒng)的線程數(shù)是多少,可以通過?
top -H
?查看。左上角的 Threads 的數(shù)量顯示是 14553,與?
threads-max
?內(nèi)核參數(shù)的值相同,所以我們可以認為是因為這個參數(shù)導致無法繼續(xù)創(chuàng)建線程。那么,我們可以把 threads-max 參數(shù)設(shè)置成?
99999
:
echo?99999?>?/proc/sys/kernel/threads-max
設(shè)置完 threads-max 參數(shù)后,我們重新跑測試線程數(shù)的程序,運行后結(jié)果如下圖:可以看到,當進程創(chuàng)建了 32326 個線程后,就無法繼續(xù)創(chuàng)建里,且報錯是無法繼續(xù)申請內(nèi)存。此時的上限個數(shù)很接近?
pid_max
?內(nèi)核參數(shù)的默認值(32768),那么我們可以嘗試將這個參數(shù)設(shè)置為 99999:
echo?99999?>?/proc/sys/kernel/pid_max
設(shè)置完 pid_max 參數(shù)后,繼續(xù)跑測試線程數(shù)的程序,運行后結(jié)果創(chuàng)建線程的個數(shù)還是一樣卡在了 32768 了。當時我也挺疑惑的,明明 pid_max 已經(jīng)調(diào)整大后,為什么線程個數(shù)還是上不去呢?后面經(jīng)過查閱資料發(fā)現(xiàn),
max_map_count
?這個內(nèi)核參數(shù)也是需要調(diào)大的,但是它的數(shù)值與最大線程數(shù)之間有什么關(guān)系,我也不太明白,只是知道它的值是會限制創(chuàng)建線程個數(shù)的上限。然后,我把 max_map_count 內(nèi)核參數(shù)也設(shè)置成后 99999:
echo?99999?>?/proc/sys/kernel/pid_max?
繼續(xù)跑測試線程數(shù)的程序,結(jié)果如下圖:當創(chuàng)建差不多 5 萬個線程后,我的服務器就卡住不動了,CPU 都已經(jīng)被占滿了,畢竟這個是單核 CPU,所以現(xiàn)在是 CPU 的瓶頸了。我只有這臺服務器,如果你們有性能更強的服務器來測試的話,有興趣的小伙伴可以去測試下。接下來,我們換個思路測試下,把創(chuàng)建線程時分配的棧空間調(diào)大,比如調(diào)大為 100M,在大就會創(chuàng)建線程失敗。
ulimit?-s?1024000
設(shè)置完后,跑測試線程的程序,其結(jié)果如下:總共創(chuàng)建了 26390 個線程,然后就無法繼續(xù)創(chuàng)建了,而且該進程的虛擬內(nèi)存空間已經(jīng)高達 25T,要知道這臺服務器的物理內(nèi)存才 2G。為什么物理內(nèi)存只有 2G,進程的虛擬內(nèi)存卻可以使用 25T 呢?因為虛擬內(nèi)存并不是全部都映射到物理內(nèi)存的,程序是有
局部性的特性,也就是某一個時間只會執(zhí)行部分代碼,所以只需要映射這部分程序就好。你可以從上面那個 top 的截圖看到,雖然進程虛擬空間很大,但是物理內(nèi)存(RES)只有使用了 400M 。好了,簡單總結(jié)下:
- 32 位系統(tǒng),用戶態(tài)的虛擬空間只有 3G,如果創(chuàng)建線程時分配的??臻g是 10M,那么一個進程最多只能創(chuàng)建 300 個左右的線程。
- 64 位系統(tǒng),用戶態(tài)的虛擬空間大到有 128T,理論上不會受虛擬內(nèi)存大小的限制,而會受系統(tǒng)的參數(shù)或性能限制。
辛苦了你們了,深夜還讓你們跑來看技術(shù)文。
怪我!