對Docker-java項目進行jvm調優(yōu)-內存方式
1. 分析內存使用情況
1.1 進入docker容器
# 查看容器列表 docker ps # 根據容器name或id進入容器命令行終端 docker exec -it <container_name> bash
1.2 通過jps查看當前java進程列表
三種不同詳細程度的進程列表(主要是獲取java進程的lvmid)
root@21e4300860c8:/# jps 1 jar 79 Jps root@21e4300860c8:/# jps -l 1 /knx-organization-service-exec.jar 127 sun.tools.jps.Jps root@21e4300860c8:/# jps -lv 1 /knx-organization-service-exec.jar -Xmx512m -Xms512m -Dspring.profiles.active=dev,jiewli -Djasypt.encryptor.password= -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=18000 139 sun.tools.jps.Jps -Dapplication.home=/usr/lib/jvm/java-8-openjdk-amd64 -Xms8m
1.3 通過jstat -gccapacity統(tǒng)計java進程的內存池容量
root@21e4300860c8:/# jstat -gccapacity -h 20 1 250 NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC 174592.0 174592.0 174592.0 7680.0 8704.0 157184.0 349696.0 349696.0 349696.0 349696.0 0.0 1136640.0 98280.0 0.0 1048576.0 11904.0 46 4 174592.0 174592.0 174592.0 7680.0 8704.0 157184.0 349696.0 349696.0 349696.0 349696.0 0.0 1136640.0 98280.0 0.0 1048576.0 11904.0 46 4 174592.0 174592.0 174592.0 7680.0 8704.0 157184.0 349696.0 349696.0 349696.0 349696.0 0.0 1136640.0 98280.0 0.0 1048576.0 11904.0 46 4 174592.0 174592.0 174592.0 7680.0 8704.0 157184.0 349696.0 349696.0 349696.0 349696.0 0.0 1136640.0 98280.0 0.0 1048576.0 11904.0 46 4 174592.0 174592.0 174592.0 7680.0 8704.0 157184.0 349696.0 349696.0 349696.0 349696.0 0.0 1136640.0 98280.0 0.0 1048576.0 11904.0 46 4 174592.0 174592.0 174592.0 7680.0 8704.0 157184.0 349696.0 349696.0 349696.0 349696.0 0.0 1136640.0 98280.0 0.0 1048576.0 11904.0 46 4 174592.0 174592.0 174592.0 7680.0 8704.0 157184.0 349696.0 349696.0 349696.0 349696.0 0.0 1136640.0 98280.0 0.0 1048576.0 11904.0 46 4
說明:
- 新生代空間由
Survivor0
、Survivor1
、Eden
(幸存者空間0、幸存者空間1、伊甸園)三部分組成 OGC = sum(all OC)
,目前老年代只有一個空間,故而OGC
與OC
相等??梢蕴骄恳幌吕夏甏卸鄠€空間的時候有什么差異。- JVM參數
-Xmx512m
最大堆內存等于NGCMX + OGCMX
- JVM參數
-Xms512m
最小堆內存等于NGCMN + OGCMN
- 將堆的最小值
-Xms
參數與最大值-Xmx
參數設置為一樣即可避免堆自動擴展。因為JVM初始分配的內存由-Xms
指定,默認是物理內存的1/64
;JVM最大分配的內存由-Xmx
指定,默認是物理內存的1/4
。默認空余堆內存小于40%時,JVM就會增大堆直到-Xmx
的最大限制;空余堆內存大于70%時,JVM會減少堆直到-Xms
的最小限制。因此服務器一般設置-Xms
、-Xmx
相等以避免在每次GC后調整堆的大小。對象的堆內存由稱為垃圾回收器的自動內存管理系統(tǒng)回收。 - Full GC觸發(fā)條件:
- 調用
System.gc
時 - 老年代空間不足
- 方法區(qū)空間不足
- 通過Minor GC(新生代GC)后進入老年代的平均大小大于老年代的可用內存
- 由Eden區(qū)、From Space區(qū)向To Space區(qū)復制時,對象大小大于To Space可用內存,則把該對象轉存到老年代,且老年代的可用內存小于該對象大小
JVM參數-XX:MetaspaceSize
元空間初始化內存大小,默認是20.79MB,超過20.79MB后,每次元空間擴增都會觸發(fā)Full GC,所以這個值設大一點可以減少Full GC次數。
JVM參數-XX:MaxMetaspaceSize
元空間最大內存大小,默認是無限(超過宿主機內存視為與宿主機內存一致),設置這個值,可以避免在發(fā)生故障時占用大量宿主機內存資源,影響其他服務。
列代碼 | 列名 | 容量(kB) | 容量(MB) |
---|---|---|---|
NGCMN | 初始新生代空間容量 | 174592.0 | 170.5 |
NGCMX | 最大新生代空間容量 | 174592.0 | 170.5 |
NGC | 當前新生代容量 | 174592.0 | 170.5 |
S0C | 當前幸存者空間0容量 | 7680.0 | 7.5 |
S1C | 當前幸存者空間1容量 | 8704.0 | 8.5 |
EC | 當前Eden(伊甸園)區(qū)空間容量 | 157184.0 | 153.5 |
OGCMN | 初始老年代空間容量 | 349696.0 | 341.5 |
OGCMX | 最大老年代空間容量 | 349696.0 | 341.5 |
OGC | 當前老年代空間容量 | 349696.0 | 341.5 |
MCMN | 初始元空間容量 | 0.0 | 0 |
MCMX | 最大元空間容量 | 1136640.0 | 1,110 |
MC | 當前元空間容量 | 98280.0 | 95.9765625 |
CCSMN | 壓縮的類空間初始容量 | 0.0 | 0 |
CCSMX | 壓縮的類空間最大容量 | 1048576.0 | 1,024 |
CCSC | 當前壓縮的類空間容量 | 11904.0 | 11.625 |
YGC | 新生代GC事件數量 | 46 | |
FGC | 完整GC事件數量 | 4 |
1.4 也可以通過jstat -gc來統(tǒng)計
root@7f9fbd4518a4:/# jstat -gc -h 20 1 250 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 10240.0 10240.0 2416.0 0.0 154112.0 123601.6 349696.0 60088.4 81112.0 75681.7 9432.0 8462.8 38 1.640 3 0.431 2.071 10240.0 10240.0 2416.0 0.0 154112.0 123601.6 349696.0 60088.4 81112.0 75681.7 9432.0 8462.8 38 1.640 3 0.431 2.071 10240.0 10240.0 2416.0 0.0 154112.0 123601.6 349696.0 60088.4 81112.0 75681.7 9432.0 8462.8 38 1.640 3 0.431 2.071 10240.0 10240.0 2416.0 0.0 154112.0 123893.9 349696.0 60088.4 81112.0 75681.7 9432.0 8462.8 38 1.640 3 0.431 2.071 10240.0 10240.0 2416.0 0.0 154112.0 123893.9 349696.0 60088.4 81112.0 75681.7 9432.0 8462.8 38 1.640 3 0.431 2.071 10240.0 10240.0 2416.0 0.0 154112.0 123893.9 349696.0 60088.4 81112.0 75681.7 9432.0 8462.8 38 1.640 3 0.431 2.071 10240.0 10240.0 2416.0 0.0 154112.0 123893.9 349696.0 60088.4 81112.0 75681.7 9432.0 8462.8 38 1.640 3 0.431 2.071 10240.0 10240.0 2416.0 0.0 154112.0 123893.9 349696.0 60088.4 81112.0 75681.7 9432.0 8462.8 38 1.640 3 0.431 2.071 10240.0 10240.0 2416.0 0.0 154112.0 123893.9 349696.0 60088.4 81112.0 75681.7 9432.0 8462.8 38 1.640 3 0.431 2.071 10240.0 10240.0 2416.0 0.0 154112.0 123893.9 349696.0 60088.4 81112.0 75681.7 9432.0 8462.8 38 1.640 3 0.431 2.071
說明:
列代碼 | 列名 | KB | MB | 備注 |
---|---|---|---|---|
S0C | 幸存者空間0容量 | 10240.0 | 10 | |
S1C | 幸存者空間1容量 | 10240.0 | 10 | |
S0U | 幸存者空間1利用率 | 2416.0 | 2.35 | |
S1U | 幸存者空間1利用率 | 0.0 | 0.0 | |
EC | Eden(伊甸園)空間容量 | 154112.0 | 150.5 | |
EU | Eden(伊甸園)空間利用率 | 123601.0 | 120.70 | |
OC | 老年代空間容量 | 349696.0 | 341.5 | |
OU | 老年代空間利用率 | 60088.0 | 58.67 | |
MC | 元空間容量 | 81112.0 | 79.21 | |
MU | 元空間利用率 | 75681.7 | 73.90 | |
CCSC | 壓縮類空間容量 | 9432.0 | 9.21 | Compressed class space capacity,對應參數-XX:CompressedClassSpaceSize |
CCSU | 壓縮類空間利用率 | 8462.0 | 8.26 | |
YGC | 新生代GC事件次數 | 38 | ||
YGCT | 新生代GC事件時間 | 1.640 | ||
FGC | Full GC事件次數 | 3 | ||
FGCT | Full GC事件時間 | 0.431 | ||
GCT | 總GC時間 | 2.071 |
1.5 通過jstat -gcutil統(tǒng)計java進程的垃圾收集統(tǒng)計信息
不限制采集次數,采集過程中盡量模擬一般情況下的系統(tǒng)調用情況,直到觸發(fā)gc事件。
觸發(fā)一次新生代GC事件Minor GC
(觀察YGC
數量+1)
root@21e4300860c8:/# jstat -gcutil -h 20 1 250 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 0.00 8.68 98.93 16.94 92.54 89.70 49 1.761 4 0.908 2.669 0.00 8.68 98.96 16.94 92.54 89.70 49 1.761 4 0.908 2.669 0.00 8.68 98.96 16.94 92.54 89.70 49 1.761 4 0.908 2.669 0.00 8.68 98.96 16.94 92.54 89.70 49 1.761 4 0.908 2.669 0.00 8.68 98.96 16.94 92.54 89.70 49 1.761 4 0.908 2.669 6.80 0.00 0.00 16.95 92.52 89.72 50 1.766 4 0.908 2.673 6.80 0.00 0.74 16.95 92.52 89.72 50 1.766 4 0.908 2.673 6.80 0.00 1.27 16.95 92.52 89.72 50 1.766 4 0.908 2.673 6.80 0.00 2.67 16.95 92.52 89.72 50 1.766 4 0.908 2.673 6.80 0.00 2.67 16.95 92.52 89.72 50 1.766 4 0.908 2.673 6.80 0.00 2.67 16.95 92.52 89.72 50 1.766 4 0.908 2.673 6.80 0.00 2.67 16.95 92.52 89.72 50 1.766 4 0.908 2.673
觸發(fā)一次完整GC事件Full GC
,如果空間過大,一般情況下無法人為觸發(fā),需要通過時間等待(項目啟動時間除以FGC
可知多長時間觸發(fā)一次Full GC),一般是1小時觸發(fā)一次Full GC。
或許也可以讓survivor
空間利用率接近100%觸發(fā)Full GC。
root@21e4300860c8:/# jstat -gcutil -h 20 1 250 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 0.00 99.67 53.34 19.88 93.38 90.50 45 1.680 3 0.627 2.308 0.00 99.67 55.03 19.88 93.38 90.50 45 1.680 3 0.627 2.308 0.00 99.67 55.03 19.88 93.38 90.50 45 1.680 3 0.627 2.308 0.00 99.67 55.03 19.88 93.38 90.50 45 1.680 3 0.627 2.308 0.00 99.67 56.72 19.88 93.38 90.50 45 1.680 3 0.627 2.308 0.00 99.67 56.72 19.88 93.38 90.50 45 1.680 3 0.627 2.308 28.13 0.00 0.00 21.17 93.02 90.13 46 1.729 4 0.627 2.356 0.00 0.00 0.00 16.94 92.41 89.38 46 1.729 4 0.908 2.637 0.00 0.00 0.84 16.94 92.41 89.38 46 1.729 4 0.908 2.637 0.00 0.00 0.84 16.94 92.41 89.38 46 1.729 4 0.908 2.637 0.00 0.00 0.84 16.94 92.41 89.38 46 1.729 4 0.908 2.637 0.00 0.00 0.90 16.94 92.41 89.38 46 1.729 4 0.908 2.637 0.00 0.00 0.92 16.94 92.41 89.38 46 1.729 4 0.908 2.637 0.00 0.00 0.92 16.94 92.41 89.38 46 1.729 4 0.908 2.637
1.6 查看元空間情況
# 查看java實例的某個參數 # 查看MetaspaceSize的值,默認是-XX:MetaspaceSize=21807104(20.79 MB) jinfo -flag MetaspaceSize <jvmid> # 查看MaxMetaspaceSize的值,默認是-XX:MaxMetaspaceSize=18446744073709547520(數值超過宿主機最大內存,視為無限或與宿主機一致) jinfo -flag MaxMetaspaceSize <jvmid>
1.7 查看docker容器狀態(tài)
# 在docker容器外部(宿主機)終端 docker stats # 查看某個docker容器的狀態(tài) docker stats <container_name | id>
命令結果如下:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 38b70395877a knx-organization-service 0.49% 426.4MiB / 3.682GiB 11.31% 4.88MB / 4.35MB 47.6MB / 0B 57 7f9fbd4518a4 user-account-service 0.38% 479.4MiB / 3.682GiB 12.71% 1.84MB / 2.67MB 72.1MB / 0B 48
說明:
列名 | 描述 |
---|---|
CONTAINER ID and Name | 容器的ID和名稱 |
CPU % and MEM % | 容器正在使用的主機CPU和內存的百分比 |
MEM USAGE / LIMIT | 容器正在使用的總內存以及允許使用的總內存量 |
NET I/O | 容器通過其網絡接口發(fā)送和接收的數據量 |
BLOCK I/O | 容器已從主機上的塊設備讀取和寫入的數據量 |
PIDs | 容器創(chuàng)建的進程或線程數 |
I/O設備大致分為兩類:塊設備和字符設備。塊設備將信息存儲在固定大小的塊中,每個塊都有自己的地址。
數據塊的大小通常在512字節(jié)到32768字節(jié)之間。塊設備的基本特征是每個塊都能獨立于其它塊而讀寫。磁盤是最常見的塊設備
基于結果第一條可知:
- docker容器的內存最大限制是
3.682GiB
,實際上就是宿主機的整個內存,相當于無限制。 - docker容器已使用的內存大小是
426.4MiB
,與11.31% * 3.682GiB
一致,實際上就是容器內java進程的實際內存使用大?。ê芙咏f明docker容器除了服務外沒有多少額外內存消耗。 - docker容器內的進程或線程數為57,進程只有一個java服務,所以可以視為java服務的線程數量。
1.8 計算性能參數
已知當前老年代空間容量為341.5 MB
,再根據新生代GC和Full GC兩種事件過程采集的結果可以計算出:
- 老年代空間利用率:16.94% ~ 21.17%
- 老年代空間利用大小區(qū)間:57.8501 MB ~ 72.29555 MB
- 默認老年代占堆內存的2/3,可以計算出適合的最小堆大?。?2.29555/0.66=109.53871 MB
- 將最小堆大小往上推一個最接近的1024整除數:1024/8=128 MB
- 非堆內存不會被垃圾收集,可以視為永久代,當前使用了多少,基本上就只需要多少,根據
CCSC
往上推算最接近的1024整除數:1024/64=16 MB - 當前線程為57個,考慮到當前實例是開發(fā)環(huán)境,并發(fā)率很低,算作最大線程為100個
2. 修改JVM參數-Xmx、-Xms、-XX:MetaspaceSize、-XX:MaxMetaspaceSize
Java8開始已經移除了永久代空間(PermGen或permanent generation)即-XX:PermSize
、-XX:MaxPermSize
兩個參數是無效的。
取而代之的是元空間(metaspace):
-XX:MetaspaceSize
:最小元空間大小:并非初始化元空間大小,元空間一開始是0,并且不斷擴增,直到MetaspaceSize,都不會觸發(fā)Full GC,而一旦擴增超過MetaspaceSize后,每次擴增都會觸發(fā)Full GC-XX:MaxMetaspaceSize
:最大元空間大小:擴增的上限,默認是無限,設置一個值,避免一個服務因錯誤地不斷加載類而占用整個服務器的內存,從而影響其他服務的運行。
修改項目鏡像的Dockerfile
中java
命令的JVM參數
java \ -Xmx128m \ -Xms128m \ -XX:MetaspaceSize=128m \ -XX:MaxMetaspaceSize=256m \ ......
并且重新構建鏡像并運行。
3. 預估Docker容器的內存限制
Max memory = [-Xmx] + [-XX:MaxMetaspaceSize] + number_of_threads * [-Xss]
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- 基于spring-boot和docker-java實現對docker容器的動態(tài)管理和監(jiān)控功能[附完整源碼下載]
- 使用Kubernetes和Docker部署Java微服務詳細代碼
- Docker中Java基礎鏡像OpenJDK和OracleJDK使用方法
- docker-compose java.net.UnknownHostException問題
- Docker啟動容器報錯:Ports are not available的解決方案
- Docker使用java項目工程的部署
- Docker部署Java應用程序的實現步驟
- Java(SpringBoot)項目打包(構建)成Docker鏡像的幾種常見方式
相關文章
使用docker部署java項目運行環(huán)境的實現步驟
本文主要介紹了使用docker部署java項目運行環(huán)境的實現步驟,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01