面試官:怎么做JDK8的內存調優?
看着面試官真誠的眼神,心中暗想看起來年紀輕輕卻提出如此直擊靈魂的問題。擦了擦額頭上汗😓,我稍微調整了一下緊張的情緒😥,對面試官說:
在內存調優之前,需要先了解JDK8的內存區域是怎么划分的:
JDK8內存結構
JDK8的內存結構主要包括程序計數器(Program Counter Register)、虛擬機棧(Java Virtual Machine Stacks)、本地方法棧(Native Method Stacks)、堆(Java Heap)、元空間(Metaspace)。
其中堆又被划分為老年代(Old Generation)、年輕代(Young Generation),其中年輕代又被划分為一個Eden區和兩個Survivor區。
一邊說着,一邊拿起筆在紙上畫了起來:
畫完以后,我又說:JDK8的內存調優主要針對的是堆和元空間。內存調優時常用到JVM參數有這些:
文章持續更新,微信搜索「萬貓學社第一時間閱讀,關注后回復「電子書」,免費獲取12本Java必讀技術書籍。
-server
JVM的server模式, 在多CPU服務器中性能可以得到更好地發揮。JDK的64位版本只支持server模式,因此在這種情況下,選項是隱式的。
-Xmx
指定堆所分配內存的最大值,等同於-XX:MaxHeapSize。不附加字母時,單位為byte,必須是1024的倍數,並且大於2MB;附加字母k或K時,表示單位為KB;附加字母m或M時,表示單位為MB;附加字母g或G時,表示單位為G。
下面的例子是使用不同的單位把堆所分配內存的最大值設置為1GB:
-Xmx1G
-Xmx1024M
-Xmx1048576K
-Xmx1073741824
-Xms
指定堆所分配內存的初始值,不附加字母時,單位為byte,必須是1024的倍數,並且大於1MB;附加字母k或K時,表示單位為KB;附加字母m或M時,表示單位為MB;附加字母g或G時,表示單位為G。如果不設置這個初始值,那么初始值將被設置為老年代和年輕代分配內存的大小的總和。
下面的例子是使用不同的單位把堆所分配的初始值設置為4GB:
-Xms4G
-Xms4096M
-Xms4194304K
-Xms4294967296
對於生產環境的部署,-Xms和-Xmx通常設置為相同的值。
-Xmn
指定堆的年輕代分配內存的初始值和最大值,不附加字母時,單位為byte;附加字母k或K時,表示單位為KB;附加字母m或M時,表示單位為MB;附加字母g或G時,表示單位為G。
堆的年輕代區域用於存放新生對象。與其他區域相比,在這個區域執行垃圾回收的頻率更高。如果年輕代的內存太小,那么將執行許多次垃圾回收。如果年輕代的內存太大,那么執行完整的垃圾回收可能需要很長時間才能完成。一般建議把年輕代的大小保持在整個堆大小的1/2到1/4之間。
下面的例子是使用不同的單位把年輕代所分配內存的初始值和最大值設置為2GB:
-Xmn2G
-Xmn2048M
-Xmn2097152K
-Xmn2147483648
除了使用-Xmn選項設置年輕代的初始值和最大值,還可以使用-XX:NewSize設置年輕代的初始值,使用-XX:MaxNewSize設置年輕代的最大值。
文章持續更新,微信搜索「萬貓學社第一時間閱讀,關注后回復「電子書」,免費獲取12本Java必讀技術書籍。
-XX:NewRatio
指定老年代和年輕代空間大小的比率。默認為2,即老年代和年輕代空間大小的比率為2:1,年輕代占整個堆內存空間大小的1/3。下面的例子是把老年代和年輕代空間大小的比率設置為1:
-XX:NewRatio=1
另外,年輕代分配內存設置的優先級如下:
- 高優先級: -XX:NewSize/-XX:MaxNewSize
- 中優先級: -Xmn
- 低優先級: -XX:NewRatio
-XX:SurvivorRatio
指定Eden區和一個Survivor區的空間大小的比率。默認為8,即Eden區和一個Survivor區的空間大小為8:1,因為一共有兩個Survivor區,所以Eden區占年輕代內存大小的80%。下面的例子是把Eden區和一個Survivor區的空間大小的比率設置為4:
-XX:SurvivorRatio=4
-XX:MetaspaceSize
指定元空間第一次觸發垃圾回收的內存大小的閾值。當元空間內存占用不斷增大,直到達到這個閾值時,就會觸發一次垃圾回收。所以,適當的增大這個閾值,會減少垃圾回收的次數。默認值根據平台而定,一般情況下大約20.8MB。下面的例子是把元空間第一次觸發垃圾回收的內存大小設置為256MB:
-XX:MetaspaceSize=256M
有一些小伙伴對這個參數有誤解,造成不必要的麻煩。重申一下:-XX:MetaspaceSize不是元空間內存大小的初始值,不是元空間內存大小的初始值,不是元空間內存大小的初始值,重要的事情說三遍。
-XX:MaxMetaspaceSize
指定元空間所分配內存的最大值,默認是沒有限制,取決於系統的可用內存量,理論上可以占滿整個系統的內存。為了避免這種慘劇,影響系統上的其他應用,需要適當設置它的大小。下面的例子是把元空間所分配內存的最大值設置為512MB:
-XX:MaxMetaspaceSize=512M
面試官微笑地說:這些常用的內存調優參數總結的不錯,可以結合這些參數寫一個內存調優實例嗎?
被面試官誇獎一下,我按捺住心中的喜悅說:當然可以。
文章持續更新,微信搜索「萬貓學社第一時間閱讀,關注后回復「電子書」,免費獲取12本Java必讀技術書籍。
內存調優實例
盡可能把堆內存的空間設置大一些,以減少垃圾回收的次數。假設服務器上的可用內存還有12GB,那么先指定堆所分配內存的最大值和初始值為8GB。一般情況下,年輕代內存大小需在整個堆大小的1/2到1/4之間,那么就指定年輕代內存大小為3GB。再把Eden區和一個Survivor區的空間大小的比率設置為4。元空間第一次觸發垃圾回收的內存大小的閾值設置為256MB,一般情況下足夠用。元空間所分配內存的最大值設置為512MB,為了避免極端情況下占用大量內存。另外,還需要明確指定JVM以server模式啟動。
內存調優的參數基本敲定,用它啟動一個名為one-more-study-0.0.1-SNAPSHOT.jar的jar文件:
java -server -Xmx8G -Xms8G -Xmn3G -XX:SurvivorRatio=4 -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M -jar one-more-study-0.0.1-SNAPSHOT.jar
如果執行jmap -heap
命令查看對應Java進程的內存配置和使用情況,應該是這樣的:
Attaching to process ID 31828, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.251-b08
using thread-local object allocation.
Parallel GC with 8 thread(s)
Heap Configuration: #堆的內存配置
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
# 堆內存的最大值
MaxHeapSize = 8589934592 (8192.0MB)
# 年輕代內存的大小
NewSize = 3221225472 (3072.0MB)
# 年輕代內存的最大值
MaxNewSize = 3221225472 (3072.0MB)
# 老年代內存的大小
OldSize = 5368709120 (5120.0MB)
# 老年代和年輕代空間大小的比率
# 因為設置Xmn參數,該設置未生效
NewRatio = 2
#Eden區和一個Survivor區的空間大小的比率
SurvivorRatio = 4
# 元空間第一次觸發垃圾回收的內存大小
MetaspaceSize = 268435456 (256.0MB)
# 元空間內存的最大值
MaxMetaspaceSize = 536870912 (512.0MB)
G1HeapRegionSize = 0 (0.0MB)
Heap Usage: # 堆的使用情況
PS Young Generation
Eden Space: # Eden區內存的使用情況
capacity = 2147483648 (2048.0MB)
used = 901945720 (860.16MB)
free = 1245537928 (1187.83MB)
42.000120505690575% used
From Space: # Survivor的From區內存的使用情況
capacity = 536870912 (512.0MB)
used = 0 (0.0MB)
free = 536870912 (512.0MB)
0.0% used
To Space: # Survivor的To區內存的使用情況
capacity = 536870912 (512.0MB)
used = 0 (0.0MB)
free = 536870912 (512.0MB)
0.0% used
PS Old Generation # 老年代內存的使用情況
capacity = 5368709120 (5120.0MB)
used = 0 (0.0MB)
free = 5368709120 (5120.0MB)
0.0% used
12047 interned Strings occupying 1045744 bytes.
聽了我的回答后,面試官對我會心一笑,我仿佛還在她的眼神中看到了一絲敬仰。正所謂:萬兩黃金容易得,知心一個也難求,欲知后事如何,且聽下回分解。
微信公眾號:萬貓學社
微信掃描二維碼
關注后回復「電子書」
獲取12本Java必讀技術書籍
