1. 前言
Java 應用運行過程中你是否遇到以下類似問題
- 為什么 Java 應用所在的 Docker 容器內(nèi)存使用量不會減少?
- 發(fā)生 OOM 后程序還能運行嗎?
- Java 應用所在的容器為什么宕機或者自動重啟了?
在回答以上問題前,我們先了解下“OOM”和“JVM 內(nèi)存管理”。本文涉及的 JVM 相關描述特指 HotSpot JDK8。
2. OOM 機制
2.1. Linux 的 OOM 機制
當系統(tǒng)內(nèi)存不足時,Linux 內(nèi)核會觸發(fā) OOM Killer(OOM 殺手)機制。OOM Killer 會嘗試找出最適合終止的進程,并向其發(fā)送 SIGKILL 信號,使其被強制終止。選擇目標進程的策略通?;谶M程的 OOM 分數(shù)(OOM Score),該分數(shù)反映了進程使用內(nèi)存的情況和重要性。 具體選擇哪個進程殺掉,有一套算分策略,分兩部分:
- 參考進程占用的內(nèi)存情況打分,進程內(nèi)存開銷是變化的,因此該值也會動態(tài)變化;
- 用戶可以設置 oom_score_adj 參數(shù),取值范圍是[-1000,1000],oom_score_adj 的值越小,進程得分越少,也就越難被殺掉。
2.2. Docker 限制內(nèi)存的原理
- Docker 基于 Linux 內(nèi)核提供的 cgroups 功能,可以限制容器在運行時使用到的資源,比如內(nèi)存、CPU 等。容器的內(nèi)存隨容器內(nèi)進程內(nèi)存使用量的增加而超過了設置的上限,在啟用 OOM killer 時(默認啟用),就會導致容器觸發(fā) linux 的 OOM 機制而被終止進程。
- Docker 可在啟動容器時使用–oom-kill-disable=true 來禁止被 OOM 殺掉,默認啟用(一般不建議禁用)。docker 的這個參數(shù)對應 cgroups 的 memory.oom_control 參數(shù)。如果開啟,進程如果嘗試申請內(nèi)存超過允許,就會被系統(tǒng) OOM killer 終止。OOM killer 在每個使用 cgroup 內(nèi)存子系統(tǒng)中都是默認開啟的。如果 OOM killer 關閉,那么進程嘗試申請的內(nèi)存超過允許,那么它就會被暫停,直到額外的內(nèi)存被釋放。
- 運行在 Docker 中的 Java 應用是一個進程,自然而然會受 Linux 內(nèi)核的 OOM 機制影響。
小知識:k8s 對資源的限制也是通過 cgroups 來實現(xiàn)的,POD 本身并沒有限制資源的能力。
2.3. JVM 的 OOM
- 官方說明,當 JVM 沒有足夠的內(nèi)存來為對象分配空間并且垃圾回收器也已經(jīng)沒有空間可回收時,就會拋出這個 Error。這個錯誤不是普通的異常,已經(jīng)嚴重到無法被應用處理。
- 發(fā)生 OutOfMemoryError(OOM)錯誤可能會導致 JVM 退出。當 JVM 的內(nèi)存不足以分配新的對象時,會拋出 OOM 錯誤。這通常是由于程序使用了過多的內(nèi)存或存在內(nèi)存泄漏導致的。在發(fā)生 OOM 錯誤后,JVM 可能會嘗試進行一些垃圾回收操作來釋放內(nèi)存,但如果沒有足夠的可用內(nèi)存,JVM 可能無法繼續(xù)正常執(zhí)行,并最終退出。
2.3.1. JVM 內(nèi)存管理機制簡述
JVM 就像一個需要一些內(nèi)存才能運行的虛擬操作系統(tǒng),而從操作系統(tǒng)請求分配內(nèi)存是一項耗時的操作。當 JVM 中的程序任務執(zhí)行完成時,雖然 GC 回收器可能回收了這部分內(nèi)存(邏輯上釋放),但大多數(shù) JVM 不會將內(nèi)存釋放回操作系統(tǒng),僅僅是釋放回 JVM 中對應的內(nèi)存區(qū)域;等到下次執(zhí)行任務時,無需再從底層操作系統(tǒng)請求內(nèi)存資源,JVM 的這種架構有助于提高性能。
JVM 中 MemoryUsage 各指標的含義
- init,JVM 啟動時向操作系統(tǒng)申請的初始內(nèi)存量。
- used,當前使用的內(nèi)存量。
- committed,保證供虛擬機使用的內(nèi)存量,這部分內(nèi)存量可能隨時間而變化(增加或減少),committed 的部分一直是大于或等于 used 的內(nèi)存量。
- max,JVM 內(nèi)存管理中可以被使用的內(nèi)存上限。
2.3.2. JVM 運行時數(shù)據(jù)區(qū)域
-
常見的圖
jmm-general.png -
整理的圖
Docker&JVM運行時內(nèi)存區(qū)域關系.png
2.3.3. 內(nèi)存溢出區(qū)域
2.3.3.1. 堆
最容易遇到內(nèi)存溢出的區(qū)域。
-
異常
java.lang.OutOfMemoryError: Java heap space -
處理方法
- 一般在事前配置好 JVM 堆溢出的自動導堆轉(zhuǎn)儲快照的參數(shù)。
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/xx/logs/heapdump.hprof - 使用 JProfiler 或者 MAT 分析堆轉(zhuǎn)儲快照;分清楚是內(nèi)存泄露還是內(nèi)存溢出。
- 內(nèi)存泄露,通過工具進一步查看泄露對象到 GC Roots 的引用鏈,一般可以比較準確定位到具體的代碼。
- 非內(nèi)存泄露,檢查 JVM 的堆參數(shù)(-Xmx 和-Xms)配置是否合理。
- 一般在事前配置好 JVM 堆溢出的自動導堆轉(zhuǎn)儲快照的參數(shù)。
2.3.3.2. 虛擬機棧和本地方法棧
HotSpot 虛擬機中并不區(qū)分虛擬機棧和本地方法棧,棧容量由-Xss 參數(shù)設定,JDK8 中默認值為 1M。
-
異常
// 異常1:棧溢出 java.lang.StackOverflowError // 異常2:服務器剩余內(nèi)存不足 java.lang.OutOfMemoryError: unable to create native thread -
處理方法
- 線程請求的棧深度大于 JVM 所允許的最大深度,檢查是否-Xss 設置過小導致或者程序問題。
- 棧幀內(nèi)存無法分配(線程大小*N>=總內(nèi)存-堆-元空間-其它內(nèi)存占用),檢查 JVM 參數(shù)配置是否合理或者程序問題導致線程過多。
2.3.3.3. 方法區(qū)
-
異常
java.lang.OutOfMemoryError: Metaspace -
處理方法
- JVM 參數(shù)配置不合理,-XX:MaxMetaspaceSize 設置過小。
- 程序問題,運行時生成大量動態(tài)類,比如使用了 CGLib 字節(jié)碼增強、用到了動態(tài)語言(如 Groovy 等)、大量 JSP 或動態(tài)產(chǎn)生 JSP 文件應用(JSP 第一次運行時需要編譯為 Java 類)、基于 OSGi 的應用(即使同一個類文件,被不同的加載器加載也會視為不同的類)等。
2.3.3.4. Compressed class space
JVM 有個功能是 CompressedOops ,目的是為了在 64bit 機器上使用 32bit 的原始對象指針(oop,ordinary object pointer,這里直接就當成指針概念理解就可以)來節(jié)約成本(減少內(nèi)存/帶寬使用),提高性能(提高 Cache 命中率)。
使用了這個壓縮功能,每個對象中的 Klass* 字段就會被壓縮成 32bit(不是所有的 oop 都會被壓縮的), Klass* 指向的 Klass 在永久代(Java7 及之前)。但是在 Java8 及之后,永久代沒了,有了一個 Metaspace,于是之前壓縮指針 Klass* 指向的這塊 Klass 區(qū)域有了一個名字 —— Compressed Class Space。Compressed Class Space 是 Metaspace 的一部分,默認大小為 1G。所以其實 Compressed Class Space 這個名字取得很誤導,壓縮的并不是 Klass,而是 Klass*。
- JDK8 中,啟用對象和類指針壓縮(默認啟用)且堆內(nèi)存-Xmx<32G,會額外分配的非堆空間;可通過參數(shù)-XX:CompressedClassSpaceSize 控制大小,在啟動的時候就限制 Class Space 的大小,默認值是 1G,啟動后不可以修改。它是 reserved 不是 committed 的內(nèi)存。
- 禁用指針壓縮(-XX:-UseCompressedOops)或者堆內(nèi)存-Xmx>=32G 時,此區(qū)域在 Metaspace 中,不獨立存在。
- 以下異常描述特指第一種獨立分配空間時的情況。
-
異常
java.lang.OutOfMemoryError: Compressed class space -
處理方法
- 一般是 JVM 參數(shù)設置不合理,可通過-XX:CompressedClassSpaceSize 控制。
- 如果是程序問題做進一步排查優(yōu)化。
2.3.3.5. Code Cache
- JIT 編譯成本地機器碼的緩存區(qū)域大小,不同 jdk 版本和機器默認值不同(一般是 240m),可由-XX:ReservedCodeCacheSize=240m 控制大小。
- 關聯(lián)參數(shù)-XX:+UseCodeCacheFlushing,代碼緩存區(qū)即將耗盡,嘗試回收一些早期編譯、很久未被調(diào)用的方法,默認打開。
-
異常
Java HotSpot(TM) 64-Bit Server VM warning: CodeCache is full. Compiler has been disabled. Java HotSpot(TM) 64-Bit Server VM warning: Try increasing the code cache size using -XX:ReservedCodeCacheSize= -
處理方法
- 通過參數(shù)-XX:ReservedCodeCacheSize 設置合理的值。
2.3.3.6. 直接內(nèi)存
直接內(nèi)存(Direct Memory)的容量大小可以通過-XX:MaxDirectMemorySize 參數(shù)來指定,如果不指定,默認與 Java 堆最大值(由-Xmx 指定)一致。
-
異常
// 異常1 java.lang.OutOfMemoryError: Direct buffer memory // 異常2 java.lang.OutOfMemoryError at sum.misc.Unsafe.allocateMemory(Native Method) -
處理方法
- 直接內(nèi)存導致的內(nèi)存溢出,一個明顯特征是在 Heap Dump 文件中不會看見明顯異常,如果程序中直接或者間接使用了 DirectMemory(典型間接使用就是 NIO),可以考慮重點檢查直接內(nèi)存方面的原因;
- 可能的原因是 JVM 參數(shù)配置不合理(比如-XX:MaxDirectMemorySize 設置不合理),或者程序問題。
-
注意事項
- 如果使用 Java 自帶的 ByteBuffer.allocateDirect(size) 或者直接 new DirectByteBuffer(capacity) , 這樣受-XX:MaxDirectMemorySize 這個 JVM 參數(shù)的限制。其實底層都是用的 Unsafe#allocateMemory,區(qū)別是對大小做了限制. 如果超出限制直接 OOM。
- 如果通過反射的方式拿到 Unsafe 的實例,然后用 Unsafe 的 allocateMemory 方法分配堆外內(nèi)存。確實不受-XX:MaxDirectMemorySize 這個 JVM 參數(shù)的限制 。所限制的內(nèi)存大小為操作系統(tǒng)的內(nèi)存。
- 如果不設置-XX:MaxDirectMemorySize 默認的話,是跟堆內(nèi)存大小保持一致。 [堆內(nèi)存大小如果不設置的話,默認為操作系統(tǒng)的 1/4, 所以 DirectMemory 的大小限制 JVM 的 Runtime.getRuntime().maxMemory()內(nèi)存大小 ]
3. 問題分析
在了解了 Linux 的 OOM 機制和 JVM 內(nèi)存管理的基本知識后,前面的 3 個問題的分析就變簡單了。
3.1. 為什么 Java 應用所在的 Docker 容器內(nèi)存使用量不會減少?
由上文中“JVM 內(nèi)存管理機制簡述”我們可以直接得到答案,減少的是 JVM 中管理的內(nèi)存,占用的操作系統(tǒng)內(nèi)存(docker 內(nèi)存)一般情況下不會減少。
- 早期,運維/工程人員老問 XX 應用的 Docker 內(nèi)存占用超過 80%了并且沒有回落,趕緊檢查下程序是不是有問題。
- 多數(shù)情況下點開 JVM 的內(nèi)存監(jiān)控面板,發(fā)現(xiàn)只是某段業(yè)務繁忙時刻 JVM used 的內(nèi)存升高,一定時間后又回落到正常水平,只是這個時候 committed 的內(nèi)存大部分情況下并不會釋放回給操作系統(tǒng)導致 Docker 內(nèi)存長期處于高位。
因此,Docker 的內(nèi)存占用并不能很好反應 JVM 真實的內(nèi)存使用情況,推薦大家看應用的內(nèi)存占用時一定要結合 JVM 的內(nèi)存監(jiān)控來看。那么有沒有辦法歸還空余內(nèi)存給操作系統(tǒng)呢?
JVM 提供了-XX:MinHeapFreeRatio 和-XX:MaxHeapFreeRatio 兩個參數(shù),用于配置這個歸還策略。
- MinHeapFreeRatio 代表當空閑區(qū)域大小下降到該值時,會進行擴容,擴容的上限為 Xmx。
- MaxHeapFreeRatio 代表當空閑區(qū)域超過該值時,會進行“縮容”,縮容的下限為 Xms。
JVM 在歸還的時候,是線性遞增歸還的,并不是一次全部歸還。這個歸還內(nèi)存的機制,在不同的垃圾回收器,甚至不同的 JDK 版本不一。以下表格摘自網(wǎng)友實測:
| JAVA 版本 | GC 回收器 | VM Options | 是否可以“歸還” |
|---|---|---|---|
| JAVA 8 | UseParallelGC(ParallerGC + ParallerOld) | -Xms100M -Xmx2G -XX:MaxHeapFreeRatio=40 | 否 |
| JAVA 8 | CMS+ParNew | -Xms100M -Xmx2G -XX:MaxHeapFreeRatio=40 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC | 是,需要 4 次 FGC 之后觸發(fā) |
| JAVA 8 | UseG1GC(G1) | -Xms100M -Xmx2G -XX:MaxHeapFreeRatio=40 -XX:+UseG1GC | 是,首次 FGC 之后觸發(fā) |
| JAVA 11 | UseG1GC(G1) | -Xms100M -Xmx2G -XX:MaxHeapFreeRatio=40 | 是,首次 FGC 之后觸發(fā) |
| JAVA 16 | UseZGC(ZGC) | -Xms100M -Xmx2G -XX:MaxHeapFreeRatio=40 -XX:+UseZGC | 否 |
其它:
- JAVA 9 后-XX:-ShrinkHeapInSteps 參數(shù)(默認啟用),在第一次 FGC 后,可以讓 JVM 以非線性遞增的方式歸還內(nèi)存。如果禁用,會立即將堆減小到目標大?。ㄊ?MaxHeapFreeRatio 限制),禁用此選項可能會遇到性能下降問題。
- JAVA 12 后的 ShenandoahGC(openJDK 特有),不需要 FGC 就能異步回收不再使用的內(nèi)存并歸還給操作系統(tǒng)。
- JAVA 12 后新增兩個 G1 參數(shù) G1PeriodicGCInterval( milliseconds ) 及 G1PeriodicGCSystemLoadThreshold,設置為 0 的話,表示禁用,可以設置定期自動觸發(fā) GC 操作,從而達到歸還內(nèi)存的目的,啟用定期 GC 可能會遇到性能下降問題。
3.2. 發(fā)生 OOM 后程序還能運行嗎?
可能處于運行中,也可能退出。 以堆內(nèi)存溢出作說明:
- 線程棧空間是線程獨享,OOM 后線程被 kill,線程棧上的空間被釋放。
- 堆空間是共享的,存在兩種情況:
- 被 Kill 掉的線程中的對象可能被該線程之外的其他線程引用,這部分被引用的對象就沒有辦法被 GC 掉,其他線程如果此時需要申請資源但是又資源又不足,那么此時其他線程就不能運行,現(xiàn)象就是系統(tǒng)會卡住,然后極端情況引起連鎖反應,外部請求持續(xù)進入,積壓的線程過多,此時是有可能觸發(fā) Linux 的 OOM。
- 被 Kill 掉的線程中的對象未被其它線程應用,這部分空間也能被釋放掉,此時程序可以正常運行。
- JVM 的其它內(nèi)存區(qū)域道理類似;當然,發(fā)生 OOM 之后可能導致應用狀態(tài)不一致,建議最好重啟。以下是幾種自動退出運行狀態(tài)的方式:
- -XX:OnOutOfMemoryError(推薦),發(fā)生 OOM 時,JVM 就會調(diào)用此參數(shù)設置的腳本,此種方式可以對 JVM 進行優(yōu)雅的重啟應用。示例:
-XX:OnOutOfMemoryError=/scripts/restart-myapp.sh - -XX:+CrashOnOutOfMemoryError,發(fā)生 OOM 時,配置此參數(shù)會導致 JVM 立即退出(非優(yōu)雅退出),并生成包含崩潰信息的文本,這些崩潰信息大多很基本,不足以對 OOM 進行故障排除(經(jīng)測驗,此參數(shù)不會影響自動導堆操作,會在導完堆轉(zhuǎn)儲之后才退出)。輸出消息示例如下:
Aborting due to java.lang.OutOfMemoryError: GC overhead limit exceeded # # A fatal error has been detected by the Java Runtime Environment: # # Internal Error (debug.cpp:308), pid=26064, tid=0x0000000000004f4c # fatal error: OutOfMemory encountered: GC overhead limit exceeded # # JRE version: Java(TM) SE Runtime Environment (8.0_181-b13) (build 1.8.0_181-b13) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode windows-amd64 compressed oops) # Failed to write core dump. Minidumps are not enabled by default on client versions of Windows # # An error report file with more information is saved as: # C:workspacetier1app-svntrunkbuggyapphs_err_pid26064.log # # If you would like to submit a bug report, please visit: # http: # - -XX:+ExitOnOutOfMemoryError,大體同上一個參數(shù),只是少了崩潰消息的輸出。
- -XX:OnOutOfMemoryError(推薦),發(fā)生 OOM 時,JVM 就會調(diào)用此參數(shù)設置的腳本,此種方式可以對 JVM 進行優(yōu)雅的重啟應用。示例:
3.3. Java 應用所在的容器為什么宕機或者自動重啟了?
被 Linux OOM-killer 殺掉了進程。
3.3.1. JVM max 內(nèi)存量 < 容器內(nèi)存上限,并且 JVM max 內(nèi)存量 < 操作系統(tǒng)可用內(nèi)存
此種情況一般不會被 Linux 的 OOM-killer 殺掉進程。
- 對應 JVM 會溢出的區(qū)域報錯,此處不贅述。
- 由上個問題可以得出 JVM 一般情況還處于運行狀態(tài),往往不會導致 Docker 停止或重啟,最可能發(fā)生的情況是 Java 應用程序卡頓。
3.3.2. JVM committed 內(nèi)存量 < 容器內(nèi)存上限,并且 JVM committed 內(nèi)存量 > 操作系統(tǒng)可用內(nèi)存
被 Linux OOM-killer 殺掉了進程。
-
應用容器被終止,docker inspect <容器>
"State": { "Status": "exited", "Running": false, "Paused": false, "Restarting": false, "OOMKilled": false, //此處為false "Dead": false, "Pid": 0, "ExitCode": 137, "Error": "", "StartedAt": "2023-06-17T07:55:15.1271987Z", "FinishedAt": "2023-06-17T07:55:34.9597495Z" } -
系統(tǒng)日志輸出 OOM 信息:
> 執(zhí)行dmesg -T 或者 egrep -i -r 'killed process' /var/log [Mon Jun 26 13:44:10 2023] Out of memory: Kill process 26727 (java) score 275 or sacrifice child [Mon Jun 26 13:44:10 2023] Killed process 26727 (java), UID 0, total-vm:14137304kB, anon-rss:8854784kB, file-rss:0kB, shmem-rss:0kB
3.3.3. JVM committed 內(nèi)存量 > 容器內(nèi)存上限,并且 JVM committed 內(nèi)存量 < 操作系統(tǒng)可用內(nèi)存
被 Linux OOM-killer 殺掉了進程。
-
系統(tǒng)日志報錯如下:
> dmesg -T|grep "out of memory" [Sat Jun 17 15:55:34 2023] Memory cgroup out of memory: Killed process 4906 (java) total-vm:4563472kB, anon-rss:119968kB, file-rss:16776kB, shmem-rss:0kB, UID:0 pgtables:1036kB oom_score_adj:0 -
查看 docker inspect <容器>
"State": { "Status": "exited", "Running": false, "Paused": false, "Restarting": false, "OOMKilled": true, //此處為true,代表內(nèi)存超過容器上限被主動kill。 "Dead": false, "Pid": 0, "ExitCode": 137, "Error": "", "StartedAt": "2023-06-17T07:55:15.1271987Z", "FinishedAt": "2023-06-17T07:55:34.9597495Z" }
3.3.4. 那么如何合理規(guī)劃內(nèi)存?
JVM 參數(shù)調(diào)優(yōu)是一個循序漸進的過程,很難做到一蹴而就。以下以常見的 SpringBoot 應用內(nèi)存分配為例,假設需要配置 2C4G 堆內(nèi)存:
- 堆內(nèi)存 4G,-Xms4G -Xmx4G,一般為了性能,防止 JVM 頻繁申請內(nèi)存,最大和最小堆內(nèi)存會設置成一樣。
- Metaspace 256m, -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=256M ,此區(qū)域和引用的 Jar、加載的類數(shù)量等有關。
- CompressedClassSpace 64m,-XX:CompressedClassSpaceSize=一般建議設置為 MaxMetaspaceSize 的 20% 左右 256*0.2=52m 左右。
- ??臻g,512m,2C4G 經(jīng)驗值并發(fā)數(shù)在 400,考慮還有一些后臺線程,按 512 個預估,jdk8 之后的??臻g默認值為 1m,則??臻g總計占用至少 512m。
- Code Cache 128m,-XX:ReservedCodeCacheSize=128m,默認值為 240m,在自己程序穩(wěn)定運行一段時間后,觀察下這塊區(qū)域的大小,進一步設置合理值。
- GC 400m,具體占用大小和實際堆內(nèi)存大小以及 GC 回收器有關,從幾十兆到幾百兆不等。Parallel GC 不會占什么內(nèi)存,G1 最多會占的內(nèi)存大小為堆內(nèi)存 10% 左右,ZGC 會最多會占內(nèi)存大小為堆內(nèi)存的 15~20% 左右額外內(nèi)存,這塊內(nèi)存比較不好估算,結合監(jiān)控持續(xù)調(diào)優(yōu)。
- Direct Memory 64m,-XX:MaxDirectMemorySize=64m,看是否用到 NIO 相關特性,結合監(jiān)控進行調(diào)優(yōu)。
總計需要設置的 Docker 內(nèi)存上限為 5.5G 左右=4096+256+64+512+128+400+64=5520m
4. 總結
- OOM 并不是 JVM 獨有,Linux 下也有 OOM 機制;需要區(qū)分好運維反饋的 OOM 是哪一種。
- 合理設置操作系統(tǒng)中各 Docker 實例的內(nèi)存上限是前提,在此前提下才能合理設置好 JVM 相關內(nèi)存分配參數(shù)。
- JVM 運行時涉及的內(nèi)存區(qū)域,不僅僅是堆、元空間,還有文中截圖中涉及的區(qū)域,都需要做好內(nèi)存的合理分配。
5. 其它你可能需要知道的事
- -XX:MaxMetaspaceSize,必須配置,默認基本是無窮大,但仍然受本地內(nèi)存大小的限制。
- -XX:MaxDirectMemorySize,程序用到直接內(nèi)存,比如 NIO 特性時,務必設置合理數(shù)值,默認 64m,達到上限會觸發(fā) Full GC。
- JDK8 默認的 GC 收集器是 Parallel Scavenge +Serial Old(PS MarkSweep),針對 Web 應用場景,建議改用 CMS 或者 G1。
- Parallel Scanvenge,新生代多線程收集器,適合在后臺運算而不需要太多交互的分析任務,會導致 STW。
- Serial Old,老年代單線程收集器,適合客戶端模式下使用,會導致 STW。
- 常用的 GC 收集器,ParNew+CMS、G1 也是會導致 STW,只是 STW 的階段不同、時長不同。(ZGC、ShenandoahGC 也一樣)
- 使用 Docker 運行 JVM 時,最好禁用 swap,當用到 swap 內(nèi)存時容易導致應用性能下降??稍谶\行容器實例時通過制定--memory-swap 等于-m 設置的內(nèi)存大小來規(guī)避 或者 Linux 禁用 swap。

