啟動(dòng)Spring Boot時(shí),如果不設(shè)置內(nèi)存參數(shù)會(huì)如何?
前言
最近正在進(jìn)行從Spring Boot往Spring Cloud上改造升級(jí)。之前部署的應(yīng)用程序比較少,還沒什么問題。當(dāng)Spring Cloud項(xiàng)目逐步新增之后,問題就爆發(fā)了,服務(wù)器內(nèi)存不夠用了。而現(xiàn)有的用戶體量也沒必要對(duì)服務(wù)器再次進(jìn)行升級(jí),于是就開始著手Spring Boot啟動(dòng)時(shí)JVM內(nèi)存配置的優(yōu)化。
服務(wù)現(xiàn)狀
由于之前服務(wù)比較少,服務(wù)器資源充足,許多服務(wù)啟動(dòng)時(shí)都未添加JVM參數(shù)(遺留問題)。結(jié)果就是每個(gè)服務(wù)啟動(dòng)都占用了1.5G-2G的內(nèi)存,有些服務(wù)的體量根本用不了這么多。那么,在Spring Boot中如果未設(shè)置JVM內(nèi)存參數(shù)時(shí),JVM內(nèi)存是如何配置的呢?
JVM默認(rèn)內(nèi)存設(shè)置
當(dāng)運(yùn)行一個(gè)Spring Boot項(xiàng)目時(shí),如果未設(shè)置JVM內(nèi)存參數(shù),Spring Boot默認(rèn)會(huì)采用JVM自身默認(rèn)的配置策略。在資源比較充足的情況下,開發(fā)者倒是不太用關(guān)心內(nèi)存的設(shè)置。但一旦涉及到資源不足,JVM優(yōu)化,那么就需要了解默認(rèn)的JVM內(nèi)存配置策略。
關(guān)于JVM內(nèi)存最常見的設(shè)置為初始堆大小(-Xms)和最大堆內(nèi)存(-Xmx)。很多人懶得去設(shè)置,而是采用JVM的默認(rèn)值。特別是在開發(fā)環(huán)境下,如果啟動(dòng)的微服務(wù)比較多,內(nèi)存會(huì)被撐爆。
而JVM默認(rèn)內(nèi)存配置策略分兩種場(chǎng)景,大內(nèi)存空間場(chǎng)景和小內(nèi)存空間場(chǎng)景(小于192M)。
以4GB內(nèi)存為例,初始堆內(nèi)存大小和最大堆內(nèi)存大小如下圖:

默認(rèn)情況下,最大堆內(nèi)存占用物理內(nèi)存的1/4,如果應(yīng)用程序超過該上限,則會(huì)拋出OutOfMemoryError異常。初始堆內(nèi)存大小為物理內(nèi)存的1/64。
如果應(yīng)用程序運(yùn)行在手機(jī)上或物理內(nèi)存小于192M時(shí),JVM默認(rèn)的初始堆內(nèi)存大小和最大堆內(nèi)存大小如下圖:

最大堆內(nèi)存為物理內(nèi)存的1/2,初始堆內(nèi)存大小為物理內(nèi)存的1/64,但當(dāng)初始堆內(nèi)存最小為8MB,則為8MB。
默認(rèn)空余堆內(nèi)存小于40%時(shí),JVM就會(huì)增大堆直到-Xmx的最大限制;空余堆內(nèi)存大于70%時(shí),JVM會(huì)減少堆到 -Xms的最小限制。因此,服務(wù)器一般設(shè)置-Xms、-Xmx相等以避免在每次GC后調(diào)整堆的大小。對(duì)象的堆內(nèi)存由稱為垃圾回收器的自動(dòng)內(nèi)存管理系統(tǒng)回收。
其中最大堆內(nèi)存是JVM使用內(nèi)存的上限,實(shí)際運(yùn)行過程中使用多少便是多少。默認(rèn),分配給年輕代的最大空間量是堆總大小的三分之一。
針對(duì)最開始的問題,如果每個(gè)程序都按照默認(rèn)配置啟動(dòng),一臺(tái)服務(wù)器上部署多個(gè)應(yīng)用時(shí),就會(huì)出現(xiàn)內(nèi)存吃緊的情況,造成一定的浪費(fèi)。最簡(jiǎn)單的操作就是在執(zhí)行java -jar啟動(dòng)時(shí)添加上對(duì)應(yīng)的jvm內(nèi)存設(shè)置參數(shù)。
java -Xms64m -Xmx128m -jar xxx.jar切記參數(shù)要防止-jar參數(shù)之前。否則會(huì)被當(dāng)做系統(tǒng)參數(shù)而無(wú)效。
當(dāng)然在排查JVM的使用情況時(shí),還會(huì)用到以下相關(guān)操作。
查看系統(tǒng)默認(rèn)內(nèi)存設(shè)置
通過上面的描述我們可以看到,不同的系統(tǒng)配置,JVM使用的內(nèi)存是不同的。我們可以通過Java命令自帶的功能來(lái)查看默認(rèn)的內(nèi)存設(shè)置。
在Linux操作系統(tǒng)下,輸入如下命令:
java -XX:+PrintFlagsFinal -version | grep HeapSize在Windows操作系統(tǒng)下,輸入如下命令:
java -XX:+PrintFlagsFinal -version | findstr HeapSize查看運(yùn)行時(shí)內(nèi)存情況
當(dāng)應(yīng)用程序運(yùn)行時(shí),如果我們想查看程序的運(yùn)行情況,可通過以下幾種方式來(lái)查詢不同維度的數(shù)據(jù)。
查看正在運(yùn)行的jvm服務(wù)
可以通過jps命令查看正在運(yùn)行的jvm服務(wù)。
:~ apple$ jps51972 EurekaApplication51973 Launcher51976 Jps
其中前面的數(shù)字為進(jìn)程號(hào)。
查看某進(jìn)程的JVM情況
可以使用jstat命令查詢具體進(jìn)程的GC情況。
appledeMacBook-Pro:~ apple$ jstat -gc 51972 5000S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT14336.0 14336.0 0.0 14312.7 145920.0 100055.3 175104.0 17500.2 52136.0 48999.3 7600.0 6958.7 9 0.058 2 0.050 0.10714336.0 14336.0 0.0 14312.7 145920.0 100055.3 175104.0 17500.2 52136.0 48999.3 7600.0 6958.7 9 0.058 2 0.050 0.10714336.0 14336.0 0.0 14312.7 145920.0 100055.3 175104.0 17500.2 52136.0 48999.3 7600.0 6958.7 9 0.058 2 0.050 0.107
其中51972是進(jìn)程號(hào),5000為刷新時(shí)間。
對(duì)應(yīng)數(shù)據(jù)項(xiàng)的含義:
S0C:年輕代中第一個(gè)survivor(幸存區(qū))的容量 (字節(jié))
S1C:年輕代中第二個(gè)survivor(幸存區(qū))的容量 (字節(jié))
S0U:年輕代中第一個(gè)survivor(幸存區(qū))目前已使用空間 (字節(jié))
S1U:年輕代中第二個(gè)survivor(幸存區(qū))目前已使用空間 (字節(jié))
EC:年輕代中Eden(伊甸園)的容量 (字節(jié))
EU:年輕代中Eden(伊甸園)目前已使用空間 (字節(jié))
OC:Old代的容量 (字節(jié))
OU:Old代目前已使用空間 (字節(jié))
YGC:從應(yīng)用程序啟動(dòng)到采樣時(shí)年輕代中g(shù)c次數(shù)
YGCT:從應(yīng)用程序啟動(dòng)到采樣時(shí)年輕代中g(shù)c所用時(shí)間(s)
FGC:從應(yīng)用程序啟動(dòng)到采樣時(shí)old代(全gc)gc次數(shù)
FGCT:從應(yīng)用程序啟動(dòng)到采樣時(shí)old代(全gc)gc所用時(shí)間(s)
GCT:從應(yīng)用程序啟動(dòng)到采樣時(shí)gc用的總時(shí)間(s)
查看堆棧使用情況
通過jmap命令來(lái)查看堆棧的使用情況。
~]# jmap -heap 10471Attaching to process ID 10471, please wait...Debugger attached successfully.Server compiler detected.JVM version is 25.262-b10using thread-local object allocation.Parallel GC with 2 thread(s)Heap Configuration:MinHeapFreeRatio = 0MaxHeapFreeRatio = 100MaxHeapSize = 2051014656 (1956.0MB)NewSize = 42991616 (41.0MB)MaxNewSize = 683671552 (652.0MB)OldSize = 87031808 (83.0MB)NewRatio = 2SurvivorRatio = 8MetaspaceSize = 21807104 (20.796875MB)CompressedClassSpaceSize = 1073741824 (1024.0MB)MaxMetaspaceSize = 17592186044415 MBG1HeapRegionSize = 0 (0.0MB)Heap Usage:PS Young GenerationEden Space:capacity = 342360064 (326.5MB)used = 128426952 (122.47748565673828MB)free = 213933112 (204.02251434326172MB)usedFrom Space:capacity = 4194304 (4.0MB)used = 3458288 (3.2980804443359375MB)free = 736016 (0.7019195556640625MB)usedTo Space:capacity = 4194304 (4.0MB)used = 0 (0.0MB)free = 4194304 (4.0MB)usedPS Old Generationcapacity = 216006656 (206.0MB)used = 86142704 (82.15208435058594MB)free = 129863952 (123.84791564941406MB)used22325 interned Strings occupying 2361920 bytes.
關(guān)于具體參數(shù)就不在這里解釋了。
小結(jié)
項(xiàng)目中往往一些被忽視的問題,深究起來(lái),排查起來(lái),反而能串聯(lián)起來(lái)一系列的知識(shí)點(diǎn)和技能,或許這就是深入思考與探索的魅力所在。你在項(xiàng)目的使用過程中是否也遇到類似的問題,是否也深入探究過么?
往期推薦
如果你覺得這篇文章不錯(cuò),那么,下篇通常會(huì)更好。添加微信好友,可備注“加群”(微信號(hào):zhuan2quan)。
和花一輩子都看不清的人,
注定是截然不同的搬磚生涯。



