精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

雙11期間服務突發(fā)異常,我是如何緊急排查進程內存虛高、容器OOM問題的?

開發(fā) 前端
一個 K8S Pod,里面只有一個 Java 進程,K8S request 和 limit memory 都是 2G,Java 進程核心參數包括:-XX:+UseZGC -Xmx1024m -Xms768m。服務啟動一段時間后,查看 Grafana 監(jiān)控數據,Pod 內存使用量約 1.5G,JVM 內存使用量約 500M,通過 jvm dump 分析沒有任何大對象,運行三五天后出現 K8S Con

今天給大家分享一篇在實際工作過程中K8S Pod 容器內 Java 進程內存分析,內存虛高以及容器 OOM 和 Java OOM 問題定位的實戰(zhàn)案例,全程實戰(zhàn),好了,不多說了,進入正題。

案例背景

一個 K8S Pod,里面只有一個 Java 進程,K8S request 和 limit memory 都是 2G,Java 進程核心參數包括:-XX:+UseZGC -Xmx1024m -Xms768m

服務啟動一段時間后,查看 Grafana 監(jiān)控數據,Pod 內存使用量約 1.5G,JVM 內存使用量約 500M,通過 jvm dump 分析沒有任何大對象,運行三五天后出現 K8S Container OOM。

首先區(qū)分下 Container OOM 和 Jvm OOM,Container OOM 是 Pod 內進程申請內存大約 K8S Limit 所致。

問題來了:

  1. Pod 2G 內存,JVM 設置了 Xmx 1G,已經預留了 1G 內存,為什么還會 Container OOM,這預留的 1G 內存被誰吃了。
  2. 正常情況下(無 Container OOM),Grafana 看到的監(jiān)控數據,Pod 內存使用量 1.5G, JVM 內存使用量 500M,差別為什么這么大。
  3. Pod 內存使用量為什么超過 Xmx 限制。

Grafana 監(jiān)控圖。

圖片圖片

統計指標

Pod 內存使用量統計的指標是 container_memory_working_set_bytes

  • container_memory_usage_bytes = container_memory_rss + container_memory_cache + kernel memory
  • container_memory_working_set_bytes = container_memory_usage_bytes - total_inactive_file(未激活的匿名緩存頁)

container_memory_working_set_bytes 是容器真實使用的內存量,也是資源限制 limit 時的 OOM 判斷依據。

另外注意 cgroup 版本差異: container_memory_cache reflects cache (cgroup v1) or file (cgroup v2) entry in memory.stat.

JVM 內存使用量統計的指標是 jvm_memory_bytes_used: heap、non-heap 以及其他 真實用量總和。下面解釋其他。

首先說結論:在 POD 內,通過 top、free 看到的指標都是不準確的,不用看了,如果要看真實的數據以 cgroup 為準。

container_memory_working_set_bytes 指標來自 cadvisor,cadvisor 數據來源 cgroup,可以查看以下文件獲取真實的內存情況。

# cgroup v2 文件地址

ll /sys/fs/cgroup/memory.*
-r--r--r-- 1 root root 0 Jan  6 16:25 /sys/fs/cgroup/memory.current
-r--r--r-- 1 root root 0 Jan  6 16:25 /sys/fs/cgroup/memory.events
-r--r--r-- 1 root root 0 Jan  6 16:25 /sys/fs/cgroup/memory.events.local
-rw-r--r-- 1 root root 0 Jan  7 11:50 /sys/fs/cgroup/memory.high
-rw-r--r-- 1 root root 0 Jan  7 11:50 /sys/fs/cgroup/memory.low
-rw-r--r-- 1 root root 0 Jan  7 11:50 /sys/fs/cgroup/memory.max
-rw-r--r-- 1 root root 0 Jan  6 16:25 /sys/fs/cgroup/memory.min
-r--r--r-- 1 root root 0 Jan  6 16:25 /sys/fs/cgroup/memory.numa_stat
-rw-r--r-- 1 root root 0 Jan  7 11:50 /sys/fs/cgroup/memory.oom.group
-rw-r--r-- 1 root root 0 Jan  6 16:25 /sys/fs/cgroup/memory.pressure
-r--r--r-- 1 root root 0 Jan  6 16:25 /sys/fs/cgroup/memory.stat
-r--r--r-- 1 root root 0 Jan  6 16:25 /sys/fs/cgroup/memory.swap.current
-r--r--r-- 1 root root 0 Jan  6 16:25 /sys/fs/cgroup/memory.swap.events
-rw-r--r-- 1 root root 0 Jan  6 16:25 /sys/fs/cgroup/memory.swap.high
-rw-r--r-- 1 root root 0 Jan  6 16:25 /sys/fs/cgroup/memory.swap.max

JVM 關于使用量和提交量的解釋。

Used Size:The used space is the amount of memory that is currently occupied by Java objects. 當前實際真的用著的內存,每個 bit 都對應了有值的。

Committed Size:The  committed size is the amount of memory guaranteed to be available for  use by the Java virtual machine. 操作系統向 JVM 保證可用的內存大小,或者說 JVM 向操作系統已經要的內存。站在操作系統的角度,就是已經分出去(占用)的內存,保證給 JVM 用了,其他進程不能用了。  由于操作系統的內存管理是惰性的,對于已申請的內存雖然會分配地址空間,但并不會直接占用物理內存,真正使用的時候才會映射到實際的物理內存,所以  committed > res 也是很可能的。

Java 進程內存分析

Pod 的內存使用量 1.5G,都包含哪些。

kernel memory 為 0,Cache 約 1100M,rss 約 650M,inactive_file 約 200M。可以看到 Cache 比較大,因為這個服務比較特殊有很多文件操作。

# cgroup v2 變量變了
cat /sys/fs/cgroup/memory.stat
anon 846118912
file 2321530880
kernel_stack 10895360
pagetables 15523840
percpu 0
sock 1212416
shmem 1933574144
file_mapped 1870290944
file_dirty 12288
file_writeback 0
swapcached 0
anon_thp 0
file_thp 0
shmem_thp 0
inactive_anon 2602876928
active_anon 176771072
inactive_file 188608512
active_file 199348224
unevictable 0
slab_reclaimable 11839688
slab_unreclaimable 7409400
slab 19249088
workingset_refault_anon 0
workingset_refault_file 318
workingset_activate_anon 0
workingset_activate_file 95
workingset_restore_anon 0
workingset_restore_file 0
workingset_nodereclaim 0
pgfault 2563565
pgmajfault 15
pgrefill 14672
pgscan 25468
pgsteal 25468
pgactivate 106436
pgdeactivate 14672
pglazyfree 0
pglazyfreed 0
thp_fault_alloc 0
thp_collapse_alloc 0

通過 Java 自帶的 Native Memory Tracking 看下內存提交量。

# Java 啟動時先打開 NativeMemoryTracking,默認是關閉的。注意不要在生產環(huán)境長期開啟,有性能損失
java -XX:NativeMemoryTracking=detail -jar
# 查看詳情
jcmd $(pgrep java) VM.native_memory detail scale=MB
# 查看 summary
jcmd $(pgrep java) VM.native_memory summary scale=MB

# 創(chuàng)建基線,然后分不同時間進行 diff 查看變化
jcmd $(pgrep java) VM.native_memory baseline
jcmd $(pgrep java) VM.native_memory detail.diff scale=MB
jcmd $(pgrep java) VM.native_memory summary.diff scale=MB

通過 Native Memory Tracking 追蹤到的詳情大致如下,關注其中每一項 committed 值。

Native Memory Tracking:

(Omitting categories weighting less than 1MB)

Total: reserved=68975MB, committed=1040MB
-                 Java Heap (reserved=58944MB, committed=646MB)
                            (mmap: reserved=58944MB, committed=646MB)

-                     Class (reserved=1027MB, committed=15MB)
                            (classes #19551)  #加載類的個數
                            (  instance classes #18354, array classes #1197)
                            (malloc=3MB #63653)
                            (mmap: reserved=1024MB, committed=12MB)
                            (  Metadata:   )
                            (    reserved=96MB, committed=94MB)
                            (    used=93MB)
                            (    waste=0MB =0.40%)
                            (  Class space:)
                            (    reserved=1024MB, committed=12MB)
                            (    used=11MB)
                            (    waste=1MB =4.63%)

-                    Thread (reserved=337MB, committed=37MB)
                            (thread #335) #線程的個數
                            (stack: reserved=336MB, committed=36MB)
                            (malloc=1MB #2018)

-                      Code (reserved=248MB, committed=86MB)
                            (malloc=6MB #24750)
                            (mmap: reserved=242MB, committed=80MB)

-                        GC (reserved=8243MB, committed=83MB)
                            (malloc=19MB #45814)
                            (mmap: reserved=8224MB, committed=64MB)

-                  Compiler (reserved=3MB, committed=3MB)
                            (malloc=3MB #2212)

-                  Internal (reserved=7MB, committed=7MB)
                            (malloc=7MB #31683)

-                     Other (reserved=18MB, committed=18MB)
                            (malloc=18MB #663)

-                    Symbol (reserved=19MB, committed=19MB)
                            (malloc=17MB #502325)
                            (arena=2MB #1)

-    Native Memory Tracking (reserved=12MB, committed=12MB)
                            (malloc=1MB #8073)
                            (tracking overhead=11MB)

-        Shared class space (reserved=12MB, committed=12MB)
                            (mmap: reserved=12MB, committed=12MB)

-                    Module (reserved=1MB, committed=1MB)
                            (malloc=1MB #4996)

-           Synchronization (reserved=1MB, committed=1MB)
                            (malloc=1MB #2482)

-                 Metaspace (reserved=97MB, committed=94MB)
                            (malloc=1MB #662)
                            (mmap: reserved=96MB, committed=94MB)

-           Object Monitors (reserved=8MB, committed=8MB)
                            (malloc=8MB #39137)

先解釋下內存參數意義:

reserved:JVM 向操作系統預約的虛擬內存地址空間,僅是地址空間預留,不消耗物理資源,防止其他進程使用這段地址范圍,類似"土地規(guī)劃許可",但尚未建房。
committed:JVM 實際分配的物理內存(RAM + Swap),真實消耗系統內存資源。

虛擬地址空間
┌──────────────────────────────┐
│         reserved=16MB        │  ← 整個預約區(qū)域
├───────────────┬──────────────┤
│ committed=13MB│ 未使用 3MB   │  ← 實際使用的物理內存
└───────────────┴──────────────┘

圖片圖片

  • Heap Heap 是 Java 進程中使用量最大的一部分內存,是最常遇到內存問題的部分,Java 也提供了很多相關工具來排查堆內存泄露問題,這里不詳細展開。Heap 與 RSS 相關的幾個重要 JVM 參數如下: Xms:Java Heap 初始內存大小。(目前我們用的百分比控制,MaxRAMPercentage) Xmx:Java Heap 的最大大小。(InitialRAMPercentage) XX:+UseAdaptiveSizePolicy:是否開啟自適應大小策略。開啟后,JVM 將動態(tài)判斷是否調整 Heap size,來降低系統負載。
  • Metaspace Metaspace 主要包含方法的字節(jié)碼,Class 對象,常量池。一般來說,記載的類越多,Metaspace 使用的內存越多。與 Metaspace 相關的 JVM 參數有: XX:MaxMetaspaceSize: 最大的 Metaspace 大小限制【默認無限制】 XX:MetaspaceSize=64M: 初始的 Metaspace 大小。如果 Metaspace 空間不足,將會觸發(fā) Full GC。 類空間占用評估,給兩個數字可供參考:10K 個類約 90M,15K 個類約 100M。 什么時候回收:分配給一個類的空間,是歸屬于這個類的類加載器的,只有當這個類加載器卸載的時候,這個空間才會被釋放。釋放 Metaspace 的空間,并不意味著將這部分空間還給系統內存,這部分空間通常會被 JVM 保留下來。 擴展:參考資料中的Java Metaspace 詳解,這里完美解釋 Metaspace、Compressed Class Space 等。
  • Thread NMT 中顯示的 Thread 部分內存與線程數與 -Xss 參數成正比,一般來說 committed 內存等于 Xss *線程數 。
  • Code JIT 動態(tài)編譯產生的 Code 占用的內存。這部分內存主要由-XX:ReservedCodeCacheSize 參數進行控制。
  • Internal Internal 包含命令行解析器使用的內存、JVMTI、PerfData 以及 Unsafe 分配的內存等等。 需要注意的是,Unsafe_AllocateMemory 分配的內存在 JDK11 之前,在 NMT 中都屬于 Internal,但是在 JDK11 之后被 NMT 歸屬到 Other 中。
  • Symbol Symbol 為 JVM 中的符號表所使用的內存,HotSpot 中符號表主要有兩種:SymbolTable 與 StringTable。 大家都知道 Java 的類在編譯之后會生成 Constant pool 常量池,常量池中會有很多的字符串常量,HotSpot  出于節(jié)省內存的考慮,往往會將這些字符串常量作為一個 Symbol 對象存入一個 HashTable 的表結構中即  SymbolTable,如果該字符串可以在 SymbolTable 中  lookup(SymbolTable::lookup)到,那么就會重用該字符串,如果找不到才會創(chuàng)建新的  Symbol(SymbolTable::new_symbol)。 當然除了 SymbolTable,還有它的雙胞胎兄弟 StringTable(StringTable 結構與 SymbolTable  基本是一致的,都是 HashTable 的結構),即我們常說的字符串常量池。平時做業(yè)務開發(fā)和 StringTable  打交道會更多一些,HotSpot 也是基于節(jié)省內存的考慮為我們提供了 StringTable,我們可以通過 String.intern  的方式將字符串放入 StringTable 中來重用字符串。
  • Native Memory Tracking Native Memory Tracking 使用的內存就是 JVM 進程開啟 NMT 功能后,NMT 功能自身所申請的內存。

觀察上面幾個區(qū)域的分配,沒有明顯的異常。

NMT 追蹤到的 是 Committed,不一定是 Used,NMT 和 cadvisor 沒有找到必然的對應的關系。可以參考 RSS,cadvisor 追蹤到 RSS 是 650M,JVM Used 是 500M,還有大約 150M 浮動到哪里去了。

因為 NMT 只能 Track JVM 自身的內存分配情況,比如:Heap 內存分配,direct byte buffer 等。無法追蹤的情況主要包括:

  • 使用 JNI 調用的一些第三方 native code 申請的內存,比如使用 System.Loadlibrary 加載的一些庫。
  • 標準的 Java Class Library,典型的,如文件流等相關操作(如:Files.list、ZipInputStream 和  DirectoryStream 等)。主要涉及到的調用是 Unsafe.allocateMemory 和  java.util.zip.Inflater.init(Native Method)。

怎么追蹤 NMT 追蹤不到的其他內存,目前是安裝了 jemalloc 內存分析工具,他能追蹤底層內存的分配情況輸出報告。

通過 jemalloc 內存分析工具佐證了上面的結論,Unsafe.allocateMemory 和 java.util.zip.Inflater.init 占了 30%,基本吻合。

圖片圖片

啟動 arthas 查看下類調用棧,在 arthas 里執(zhí)行以下命令:

# 先設置 unsafe true
options unsafe true
# 這個沒有
stack sun.misc.Unsafe allocateMemory
# 這個有
stack jdk.internal.misc.Unsafe allocateMemory
stack java.util.zip.Inflater inflate

# stack 經常追蹤不到,改用 profiler 輸出內存分配火焰圖
profiler start --event alloc --duration 600
profiler start --event Unsafe_AllocateMemory0 --duration 600

通過上面的命令,能看到 MongoDB 和 netty 一直在申請使用內存。注意:早期的 mongodb client 確實有無法釋放內存的  bug,但是在我們場景,長期觀察會發(fā)現內存申請了逐漸釋放了,沒有持續(xù)增長?;氐介_頭的 ContainerOOM  問題,可能一個原因是流量突增,MongoDB 申請了更多的內存導致 OOM,而不是因為內存不釋放。

ts=2022-12-29 21:20:01;thread_name=ForkJoinPool.commonPool-worker-1;id=22;is_daemnotallow=true;priority=1;TCCL=jdk.internal.loader.ClassLoaders$AppClassLoader@1d44bcfa
    @jdk.internal.misc.Unsafe.allocateMemory()
        at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:125)
        at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:332)
        at sun.nio.ch.Util.getTemporaryDirectBuffer(Util.java:243)
        at java.net.Socket$SocketOutputStream.write(Socket.java:1035)
        at com.mongodb.internal.connection.SocketStream.write(SocketStream.java:99)
        at com.mongodb.internal.connection.InternalStreamConnection.sendMessage(InternalStreamConnection.java:426)
        at com.mongodb.internal.connection.UsageTrackingInternalConnection.sendAndReceive(UsageTrackingInternalConnection.java:99)
        at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection.sendAndReceive(DefaultConnectionPool.java:444)
        ………………………………
        at com.mongodb.MongoClientExt$1.execute(MongoClientExt.java:42)
        ………………………………

另外,arthas 自帶的 profiler 有時候經常追蹤失敗,可以切換到原始的 async-profiler ,用他來追蹤“其他”內存分配比較有效。

總結 Java 進程內存占用:Total=heap + non-heap + 上面說的這個其他。

jemalloc

jemalloc 是一個比 glibc malloc 更高效的內存池技術,在 Facebook 公司被大量使用,在 FreeBSD 和 FireFox  項目中使用了 jemalloc 作為默認的內存管理器。使用 jemalloc 可以使程序的內存管理性能提升,減少內存碎片。

比如 Redis 內存分配默認使用的 jemalloc,早期版本安裝 redis 是需要手動安裝 jemalloc 的,現在 redis 應該是在編譯期內置好了。

原來使用 jemalloc 是為了分析內存占用,通過 jemalloc 輸出當前內存分配情況,或者通過 diff 分析前后內存差,大概能看出內存都分給睡了,占了多少,是否有內存無法釋放的情況。

后來參考了這個文章,把 glibc 換成 jemalloc 帶來性能提升,降低內存使用,決定一試。

how we’ve reduced memory usage without changing any code:https://blog.malt.engineering/java-in-k8s-how-weve-reduced-memory-usage-without-changing-any-code-cbef5d740ad

Decreasing RAM Usage by 40% Using jemalloc with Python & Celery: https://zapier.com/engineering/celery-python-jemalloc/

一個服務,運行一周,觀察效果。

使用 Jemalloc 之前: 

圖片圖片

使用 Jemalloc 之后(同時調低了 Pod 內存): 

圖片圖片

注:以上結果未經生產長期檢驗。

內存交還給操作系統

注意:下面的操作,生產環(huán)境不建議這么干。

默認情況下,OpenJDK 不會主動向操作系統退還未用的內存(不嚴謹)??吹谝粡埍O(jiān)控的圖,會發(fā)現運行一段時間后,Pod 的內存使用量一直穩(wěn)定在 80%–90%不再波動。

其實對于 Java 程序,浮動比較大的就是 heap 內存。其他區(qū)域 Code、Metaspace 基本穩(wěn)定

# 執(zhí)行命令獲取當前 heap 情況
jhsdb jmap --heap --pid $(pgrep java)
#以下為輸出
Attaching to process ID 7, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 17.0.5+8-LTS

using thread-local object allocation.
ZGC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 1287651328 (1228.0MB)
   NewSize                  = 1363144 (1.2999954223632812MB)
   MaxNewSize               = 17592186044415 MB
   OldSize                  = 5452592 (5.1999969482421875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 22020096 (21.0MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
 ZHeap          used 310M, capacity 710M, max capacity 1228M

Java 內存不交還,幾種情況:

  • Xms 大于實際需要的內存,比如我們服務設置了 Xms768M,但是實際上只需要 256,高峰期也就 512,到不了 Xms 的值也就無所謂歸還。 

圖片圖片

  • 上面 jmap 的結果,可以看到 Java 默認的配置 MaxHeapFreeRatio=70,這個 70% Free 幾乎很難達到。(另外注意 Xmx==Xms 的情況下這兩個參數無效,因為他怎么擴縮都不會突破 Xms 和 Xmx 的限制)
MinHeapFreeRatio         = 40
空閑堆空間的最小百分比,計算公式為:HeapFreeRatio =(CurrentFreeHeapSize/CurrentTotalHeapSize) * 100,值的區(qū)間為 0 到 100,默認值為 40。如果 HeapFreeRatio < MinHeapFreeRatio,則需要進行堆擴容,擴容的時機應該在每次垃圾回收之后。

MaxHeapFreeRatio         = 70
空閑堆空間的最大百分比,計算公式為:HeapFreeRatio =(CurrentFreeHeapSize/CurrentTotalHeapSize) * 100,值的區(qū)間為 0 到 100,默認值為 70。如果 HeapFreeRatio > MaxHeapFreeRatio,則需要進行堆縮容,縮容的時機應該在每次垃圾回收之后。

對于 ZGC,默認是交還給操作系統的。可通過 -XX:+ZUncommit -XX:ZUncommitDelay=300 這兩個參數控制(不再使用的內存最多延遲 300s 歸還給 OS,線下環(huán)境可以改小點)。

經過調整后的服務,內存提交在 500–800M 之間浮動,不再是一條直線。

圖片圖片

內存分析工具速覽

使用 Java 自帶工具 dump 內存。

jcmd <pid> GC.heap_dump <file-path>
# 這個命令執(zhí)行,JVM 會先觸發(fā) gc,然后再統計信息。
jmap -dump:live,format=b,file=/opt/tomcat/logs/dump.hprof <pid>
# dump all
jmap -dump:format=b,file=/opt/tomcat/logs/dump.hprof <pid>

使用 jmap 輸出內存占用概覽。

jmap -histo 1 | head -n 500

使用 async-profiler 追蹤 native 內存分配,輸出火焰圖。

async-profiler/bin/asprof -d 600 -e Unsafe_AllocateMemory0 -f /opt/tomcat/logs/unsafe_allocate.html <pid>

使用 vmtouch 查看和清理 Linux 系統文件緩存。

# 查看文件或文件夾占了多少緩存
vmtouch /files
vmtouch /dir
# 遍歷文件夾輸出詳細占用
vmtouch -v /dir

# 清空緩存
vmtouch -e /dir

pmap 查看內存內容

使用 pmap 查看當前內存分配,如果找到了可疑的內存塊,可以通過 gdb 嘗試解析出內存塊中的內容。

# pmap 查看內存,先不要排序,便于找出連續(xù)的內存塊(一般是 2 個一組)
# pmap -x <pid> | sort -n -k3
pmap -x $(pgrep java)
# 舉例說明在上面發(fā)現有 7f6737dff000 開頭的內存塊可能異常,一般都是一個或多個一組,是連續(xù)的內存
cat /proc/$(pgrep java)/smaps > logs/smaps.txt
gdb attach $(pgrep java)
# dump 的起始地址,基于上面 smaps.txt 找到的內容,地址加上 0x 前綴
dump memory /opt/tomcat/logs/gdb-test.dump 0x7f6737dff000 0x7f6737e03000
# 嘗試將 dump 文件內容轉成可讀的 string,其中 -10 是過濾長度大于 10 的,也可以不過濾
strings -10 /opt/tomcat/logs/gdb-test.dump
# 如果幸運,能在上面的 strings 中找到你的 Java 類或 Bean 內容,如果不幸都是一堆亂碼,可以嘗試擴大 dump 內存塊,多找?guī)讉€連續(xù)的塊試試

# pmap 按大小降序排序并過濾大于 1000 KB 的項
pmap -x $(pgrep java) | awk 'NR>2 && !/total/ {print $2, $0}' | sort -k1,1nr | cut -d' ' -f2- | awk '$2 > 1000'

識別 Linux 節(jié)點上的 cgroup 版本

cgroup 版本取決于正在使用的 Linux 發(fā)行版和操作系統上配置的默認 cgroup 版本。 要檢查你的發(fā)行版使用的是哪個 cgroup 版本,請在該節(jié)點上運行 stat -fc %T /sys/fs/cgroup/ 命令:

stat -fc %T /sys/fs/cgroup/

對于 cgroup v2,輸出為 cgroup2fs。

對于 cgroup v1,輸出為 tmpfs。

問題原因分析和調整

回到開頭問題,通過上面分析,2G 內存,RSS 其實占用 600M,為什么最終還是 ContainerOOM 了。

  1. kernel memory 為 0,排除 kernel 泄漏的原因。下面的參考資料里介紹了 kernel 泄露的兩種場景。
  2. Cache 很大,說明文件操作多。搜了一下代碼,確實有很多 InputStream 調用沒有顯式關閉,而且有的 InputSteam Root 引用在  ThreadLocal 里,ThreadLocal 只 init 未 remove。 但是,ThreadLocal  的引用對象是線程池,池不回收,所以這部分可能會無法關閉,但是不會遞增,但是 cache 也不能回收。 優(yōu)化辦法:ThreadLocal 中對象是線程安全的,無數據傳遞,直接干掉 ThreadLocal;顯式關閉  InputStream。運行一周發(fā)現 cache 大約比優(yōu)化前低 200–500M。 ThreadLocal 引起內存泄露是 Java 中很經典的一個場景,一定要特別注意。
  3. 一般場景下,Java 程序都是堆內存占用高,但是這個服務堆內存其實在 200-500M 之間浮動,我們給他分了 768M,從來沒有到過這個值,所以調低 Xms。留出更多內存給 JNI 使用。
  4. 線下環(huán)境內存分配切換到 jemalloc,長期觀察大部分效果可以,但是對部分應用基本沒有效果。

經過上述調整以后,線下環(huán)境 Pod 內存使用量由 1G 降到 600M 作用。線上環(huán)境內存使用量在 50%–80%之間根據流量大小浮動,原來是 85% 居高不小。

不同 JVM 參數內存占用對比

以下為少量應用實例總結出來的結果,應用的模型不同占用情況會有比較大差異,僅供對比參考。

基礎參數

中低流量時內存占用(Xmx 6G)

高流量時內存占用

Java 8 + G1

65%

85%

Java 17 + G1

60%

75%

Java 17 + ZGC

90%

95%

Java 21 + G1

40%

60%

Java 21 + ZGC

80%

90%

Java 21 + ZGC + UseStringDeduplication

85%

90%

Java 21 + ZGC + ZGenerational + UseStringDeduplication

75%

80%

總結:

  1. G1 比 ZGC 占用內存明顯減少。
  2. Java 21 比 Java 8、17 占用內存明顯偏少。
  3. Java 21 ZGC 分代后確實能降低內存。
  4. 通過 -XX:+UseStringDeduplication 啟用 String 去重后,有的應用能降低 10% 內存,有的幾乎無變化。

分享我們所使用的 Java 21 生產環(huán)境參數配置,僅供參考請根據自己應用情況選擇性使用:

  • -XX:InitialRAMPercentage=40.0 -XX:MaxRAMPercentage=70.0:按照百分比設置初始化和最大堆內存。內存充足的情況下建議設置為一樣大。
  • -XX:+UseZGC -XX:+ZUncommit -XX:ZUncommitDelay=300 -XX:MinHeapFreeRatio=10  -XX:MaxHeapFreeRatio=30:促進 Java 內存更快交還給操作系統,但同時 CPU 可能偏高。
  • -XX:+ZGenerational:啟用分代 ZGC,能降低內存占用。
  • -XX:+UseStringDeduplication:啟用 String 去重,可能降低內存占用。
  • -Xss256k:降低線程內存占用,默認 1Mb,線程比較多的情況下這個占用還是很多的。謹慎設置。
  • -XX:+ParallelRefProcEnabled:多線程并行處理 Reference,減少 GC 的 Reference 數量,減少 Young GC 時間。

關于 Java 8、17 和 21 不同 GC 更多維度的對比效果可參考: https://kstefanj.github.io/2023/12/13/jdk-21-the-gcs-keep-getting-better.html 。

Java 分析工具

  • [推薦] 在線 GC 分析工具 https://gceasy.io
  • [推薦] 在線 Thread 分析工具 https://fastthread.io
  • [推薦] 在線 Heap 分析工具 https://heaphero.io
  • [推薦] 在線 jstack 分析工具 https://jstack.review
  • [Beta] 可私有化部署 Online GC、Heap Dump、Thread、JFR 分析工具 https://github.com/eclipse/jifa

參考資料

IOTDB 線上堆外內存泄漏問題排查:https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=195728187

JVM 堆外內存問題定位: https://juejin.cn/post/6844904168549777421

java 堆外內存泄漏排查: https://javakk.com/1158.html

責任編輯:武曉燕 來源: 冰河技術
相關推薦

2025-09-19 09:39:26

2022-11-10 16:39:59

架構系統技術架構

2019-11-05 08:24:34

JavaOOM快速定位

2024-10-10 15:32:51

2021-06-28 08:00:00

Python開發(fā)編程語言

2021-02-26 13:35:46

JavaCPU內存

2019-01-29 08:41:16

MySQL性能突發(fā)事件

2020-07-08 09:50:37

Java內存快速定位

2025-06-11 02:02:00

2017-08-21 23:50:45

線上內存OOM

2018-03-14 09:44:56

阿里容器平臺

2021-08-07 07:23:08

Webpack中間件模型

2018-08-10 15:00:42

服務器內存排查

2018-11-06 12:12:00

MySQL內存排查

2020-09-29 07:59:22

CPU系統性能

2021-09-26 15:36:55

華為云GaussDB

2019-04-29 14:23:46

Java服務器CPU

2019-08-06 08:55:22

PHP數據庫Linux

2018-01-12 14:35:00

Linux進程共享內存
點贊
收藏

51CTO技術棧公眾號

国产探花在线观看视频| 国内揄拍国内精品少妇国语| 婷婷六月天在线| 麻豆tv免费在线观看| 国产精选一区二区三区| 九九热这里只有精品6| 国产精品果冻传媒| 美女一区网站| 亚洲婷婷国产精品电影人久久| av成人综合网| 夜夜爽妓女8888视频免费观看| 天天射天天综合网| 亚洲电影天堂av| 亚洲一区在线不卡| heyzo高清在线| 国产精品嫩草影院com| 不卡的av一区| 在线观看中文字幕av| 国产又粗又长又大视频| 日韩偷拍自拍| 老牛影视一区二区三区| 伊人精品在线观看| www.黄色网| 国产精品美女午夜爽爽| 91久久青草| 亚洲精品国久久99热| 美国av一区二区三区| 国产v片在线观看| 久久夜色精品| 97在线免费视频| 丰满少妇高潮久久三区| 日韩av有码| 亚洲欧美国产日韩中文字幕| 一级黄色免费视频| 国产视频网站一区二区三区| 欧美在线观看禁18| 成人在线免费观看av| 成人看片免费| 一区免费观看视频| 天天人人精品| 国产私拍精品| 国产丝袜在线精品| 久久一区二区精品| 日本精品一二区| 懂色av一区二区三区蜜臀| 国产欧美久久久久久| 成年人视频免费| 欧美一级一区| 欧美与欧洲交xxxx免费观看 | 日韩黄在线观看| 中文字幕一区二区三区人妻在线视频| 日韩伦理一区二区| 欧美精品自拍偷拍| 91精品999| 亚洲我射av| 欧美二区三区的天堂| 亚洲精品视频导航| 国产成人77亚洲精品www| 日本精品免费观看高清观看| 妺妺窝人体色www在线小说| 51漫画成人app入口| 亚洲成av人综合在线观看| 加勒比成人在线| bbw在线视频| 黑人精品xxx一区| 97国产在线播放| 91av亚洲| 欧美日韩国产a| 色婷婷一区二区三区在线观看| 在线免费观看亚洲| 欧美一区二区久久久| 在线观看欧美一区二区| jizz18欧美18| 精品一区二区三区四区在线| 国产精品扒开腿做爽爽| 欧美系列电影免费观看| 日韩中文在线不卡| 黄页网站免费观看| 欧美亚洲一区二区三区| 国产一区二区在线免费视频| 精品人妻一区二区三区蜜桃| 暴力调教一区二区三区| 麻豆91蜜桃| a√资源在线| 亚洲激情图片小说视频| 91好吊色国产欧美日韩在线| 蜜桃精品在线| 欧美一区日韩一区| 成人免费无码大片a毛片| 精品国产乱码久久久| 久久视频在线播放| 黄色片视频网站| 强制捆绑调教一区二区| 98国产高清一区| 美女做暖暖视频免费在线观看全部网址91 | 国产精品69av| www.天天干.com| 国产午夜亚洲精品羞羞网站| 91精品一区二区三区四区| 天堂8中文在线最新版在线| 欧美特级限制片免费在线观看| 三上悠亚 电影| 精品国产a一区二区三区v免费| 欧美日韩福利电影| 中文字幕av第一页| 成人动漫一区二区在线| 亚洲狠狠婷婷综合久久久| av电影在线免费| 欧美三级乱人伦电影| 欧美精品欧美极品欧美激情| 婷婷另类小说| 国产国语videosex另类| 欧美熟妇另类久久久久久不卡| 国产精品久久福利| 日韩avxxx| 超碰在线亚洲| 不卡av电影在线观看| 亚洲 欧美 日韩 在线| 国产成人av一区二区三区在线| 日本一区二区免费看| 懂色av一区| 4438亚洲最大| 免费看的黄色网| 免费永久网站黄欧美| 国产91一区二区三区| 免费观看在线午夜影视| 欧洲国内综合视频| 国产乱了高清露脸对白| 狠狠综合久久av一区二区老牛| 国产欧美日韩专区发布| 韩国中文免费在线视频| 黄色精品一区二区| 亚洲美女精品视频| 亚洲综合婷婷| 91久久久国产精品| 四虎久久免费| 欧美视频一区二区三区在线观看| 中出视频在线观看| 在线免费高清一区二区三区| 99久久精品无码一区二区毛片| 美女隐私在线观看| 欧美日韩视频专区在线播放| 天天干天天舔天天操| 日韩高清在线观看| 日韩高清在线播放| 日本在线视频一区二区| 亚洲人成在线观看网站高清| 久久一区二区三区视频| 99久久99久久免费精品蜜臀| 国产原创popny丨九色 | 亚洲第一福利在线观看| 久青草视频在线观看| 国产成人h网站| 亚洲色成人www永久在线观看 | 大桥未久在线视频| 亚洲成年人影院在线| 精品视频一区二区在线观看| 丁香天五香天堂综合| 丁香六月激情婷婷| 麻豆精品99| 欧美怡春院一区二区三区| 可以在线观看的av网站| 91成人在线观看喷潮| 亚洲av毛片基地| 激情偷乱视频一区二区三区| 超碰10000| 久久99精品国产自在现线| 欧美亚洲成人网| 成人免费在线电影| 911精品国产一区二区在线| 亚洲欧美一区二区三区四区五区| 国产成人99久久亚洲综合精品| 亚洲色成人www永久在线观看| 乱亲女h秽乱长久久久| 国产成人精品日本亚洲专区61| 成黄免费在线| 91麻豆精品国产91久久久久| 精品视频在线观看免费| 久久久精品影视| 久久久久久久久久一区| 欧美日韩精选| 欧美日韩精品免费看| aaaa欧美| 国产+人+亚洲| 成人免费一区二区三区视频网站| 欧美一区二区三区的| 国产午夜精品无码| 日本一区二区三区国色天香 | 99久久精品国产观看| 国产精品69页| 欧美国产激情| 鲁片一区二区三区| 精品一区二区三区中文字幕视频| 2021国产精品视频| 久久77777| 亚洲精品中文字幕有码专区| 911美女片黄在线观看游戏| 亚洲国产精品久久不卡毛片| 国产成人免费观看网站| 成人在线综合网站| 亚洲精品久久久久久宅男| 国产精品久久久一区二区| 亚洲亚洲精品三区日韩精品在线视频| 国产成人精品福利| 国产在线一区二区三区| 日韩av影片| 精品中文字幕视频| av在线电影免费观看| 日韩av在线精品| 国产片高清在线观看| 91久久线看在观草草青青| www.youjizz.com亚洲| 国产精品女主播av| 黄色在线观看av| 国产成人午夜精品影院观看视频| 久久久精品麻豆| 国产欧美精品| www.18av.com| 99国产精品免费视频观看| 欧美大香线蕉线伊人久久| 在线视频亚洲欧美中文| 成人黄色中文字幕| 欧美精品高清| 国产97在线亚洲| 九色porny丨国产首页在线| 欧美老女人性视频| jizz性欧美10| 久久久99免费视频| 色网站在线看| 最近2019年日本中文免费字幕| 视频国产在线观看| 亚洲第一福利在线观看| 亚洲国产精品国自产拍久久| 欧美精品日韩精品| 中文字幕天堂在线| 色综合咪咪久久| 日日噜噜噜噜人人爽亚洲精品| 亚洲成av人片一区二区梦乃| 国产一级特黄a高潮片| 一区二区三区不卡视频| 人妻少妇精品一区二区三区| 亚洲乱码国产乱码精品精98午夜| 日韩亚洲欧美中文字幕| 中文av一区二区| 欧美日韩国产一二三区| 国产精品日产欧美久久久久| 纪美影视在线观看电视版使用方法| 久久日韩粉嫩一区二区三区| aaaaa级少妇高潮大片免费看| 99国产精品久久久| 亚洲av无码一区二区三区网址 | www亚洲色图| 国产欧美一区二区在线| 91视频免费在观看| 国产精品青草久久| 糖心vlog免费在线观看| 亚洲另类春色国产| 久久精品第一页| 亚洲国产三级在线| 在线观看亚洲天堂| 色欧美片视频在线观看| 中文字幕制服诱惑| 欧美一区在线视频| 神马午夜电影一区二区三区在线观看| 日韩国产高清污视频在线观看| 亚洲欧洲视频在线观看| 亚洲天堂av电影| 日本美女在线中文版| 蜜臀久久99精品久久久无需会员 | 欧美视频在线免费| 免费看毛片网站| 欧美日精品一区视频| 国产视频在线观看视频| 亚洲成在人线av| 日韩av成人| 色综合伊人色综合网| 在线你懂的视频| 欧美性资源免费| 精品女同一区二区三区在线观看| 成人黄色生活片| 里番精品3d一二三区| 日韩亚洲视频| 欧美日韩国产探花| 苍井空浴缸大战猛男120分钟| 精品在线播放免费| 男人的天堂影院| 国产精品全国免费观看高清| 国产精品成人久久| 欧美午夜片在线看| 亚洲成人中文字幕在线| 亚洲欧美制服综合另类| 国产精品剧情| 日韩免费不卡av| 欧美二区观看| 日本一区视频在线播放| 欧美视频福利| 国产免费又粗又猛又爽| 成人丝袜18视频在线观看| xxxx日本黄色| 亚洲国产一区二区视频| 中文字幕人妻色偷偷久久| 亚洲国产精品久久久| 日本中文字幕在线视频| 97国产在线观看| 成人午夜888| 视频三区二区一区| 99精品视频免费全部在线| 91日韩精品视频| 久久久久久久久久久99999| 久久久综合久久| 欧美日本在线播放| 青青青免费视频在线2| 久久久久亚洲精品成人网小说| 国产a亚洲精品| 欧美国产一二三区| 亚洲午夜电影| av在线网站免费观看| 国产精品拍天天在线| 国产午夜麻豆影院在线观看| 精品国产123| av毛片在线看| 成人国产精品久久久| 欧美日韩精品在线一区| 香港三级韩国三级日本三级| 国产成a人无v码亚洲福利| www.xx日本| 在线观看一区日韩| 亚洲三级中文字幕| 97色在线视频| 91综合久久爱com| 50度灰在线观看| 国产一区不卡精品| 国产免费嫩草影院| 欧美视频中文一区二区三区在线观看 | 精品中文字幕一区二区| 调教驯服丰满美艳麻麻在线视频| 欧美日韩中国免费专区在线看| 天堂国产一区二区三区| 久久久久久91| jizz性欧美23| av日韩一区二区三区| 成人av在线一区二区| 久久av高潮av无码av喷吹| 日韩一区二区三区免费看| 粗大黑人巨茎大战欧美成人| 成人福利网站在线观看| 久久综合电影| 中文字幕 欧美日韩| 亚洲丝袜美腿综合| 国产精品无码AV| 久久精品99国产精品酒店日本| 日韩电影精品| 欧美性受xxxx黑人猛交88| 国产专区欧美精品| 免费在线黄色片| 亚洲国产精彩中文乱码av| 不卡视频观看| 日本不卡一二三区| 秋霞av亚洲一区二区三| 糖心vlog免费在线观看| 日韩欧美中文字幕制服| 麻豆蜜桃在线| 久久国产精品精品国产色婷婷| 久久深夜福利| 黄色一级片一级片| 欧美一区二区三区人| av中文字幕电影在线看| 久久久综合香蕉尹人综合网| 石原莉奈在线亚洲二区| 伊人久久久久久久久久久久久久| 日韩一区二区三区免费看| 国产伦理精品| 日韩视频专区| 国产精品系列在线播放| 成年人免费看毛片| 亚洲欧洲国产一区| 国产精品xnxxcom| 9色porny| 国产目拍亚洲精品99久久精品| 91精品人妻一区二区三区果冻| 欧美日韩成人网| 美女久久久久| 青青草原播放器| 欧美日韩在线看| 欧美69xxx| 精品无码久久久久国产| 蜜臀国产一区二区三区在线播放| 国产又黄又爽又无遮挡| 日韩精品视频在线免费观看| 色综合.com| 欧美三级一级片| 亚洲天堂免费看| 日本黄在线观看| 亚洲影影院av| 日韩中文字幕麻豆| 久久午夜鲁丝片午夜精品| 一区二区欧美亚洲| 国产精品调教视频| 污视频网站观看| 欧美视频在线观看免费|