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

實戰:K8S Pod 容器內 Java 進程內存分析,內存虛高以及容器 OOM 和 Java OOM 問題定位

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

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

案例背景

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

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

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

問題來了:

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

Grafana 監控圖。

圖片圖片

統計指標

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,默認是關閉的。注意不要在生產環境長期開啟,有性能損失
java -XX:NativeMemoryTracking=detail -jar
# 查看詳情
jcmd $(pgrep java) VM.native_memory detail scale=MB
# 查看 summary
jcmd $(pgrep java) VM.native_memory summary scale=MB

# 創建基線,然后分不同時間進行 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 向操作系統預約的虛擬內存地址空間,僅是地址空間預留,不消耗物理資源,防止其他進程使用這段地址范圍,類似"土地規劃許可",但尚未建房。
committed:JVM 實際分配的物理內存(RAM + Swap),真實消耗系統內存資源。

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

圖片圖片

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

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

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 里執行以下命令:

# 先設置 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,但是在我們場景,長期觀察會發現內存申請了逐漸釋放了,沒有持續增長。回到開頭的 ContainerOOM  問題,可能一個原因是流量突增,MongoDB 申請了更多的內存導致 OOM,而不是因為內存不釋放。

ts=2025-09-10 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 內存): 

圖片

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

內存交還給操作系統

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

默認情況下,OpenJDK 不會主動向操作系統退還未用的內存(不嚴謹)。看第一張監控的圖,會發現運行一段時間后,Pod 的內存使用量一直穩定在 80%–90%不再波動。

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

# 執行命令獲取當前 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,值的區間為 0 到 100,默認值為 40。如果 HeapFreeRatio < MinHeapFreeRatio,則需要進行堆擴容,擴容的時機應該在每次垃圾回收之后。

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

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

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

圖片圖片

內存分析工具速覽

使用 Java 自帶工具 dump 內存。

jcmd <pid> GC.heap_dump <file-path>
# 這個命令執行,JVM 會先觸發 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 查看內存,先不要排序,便于找出連續的內存塊(一般是 2 個一組)
# pmap -x <pid> | sort -n -k3
pmap -x $(pgrep java)
# 舉例說明在上面發現有 7f6737dff000 開頭的內存塊可能異常,一般都是一個或多個一組,是連續的內存
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 內存塊,多找幾個連續的塊試試

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

識別 Linux 節點上的 cgroup 版本

cgroup 版本取決于正在使用的 Linux 發行版和操作系統上配置的默認 cgroup 版本。 要檢查你的發行版使用的是哪個 cgroup 版本,請在該節點上運行 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 也不能回收。 優化辦法:ThreadLocal 中對象是線程安全的,無數據傳遞,直接干掉 ThreadLocal;顯式關閉  InputStream。運行一周發現 cache 大約比優化前低 200–500M。 ThreadLocal 引起內存泄露是 Java 中很經典的一個場景,一定要特別注意。
  3. 一般場景下,Java 程序都是堆內存占用高,但是這個服務堆內存其實在 200-500M 之間浮動,我們給他分了 768M,從來沒有到過這個值,所以調低 Xms。留出更多內存給 JNI 使用。
  4. 線下環境內存分配切換到 jemalloc,長期觀察大部分效果可以,但是對部分應用基本沒有效果。

經過上述調整以后,線下環境 Pod 內存使用量由 1G 降到 600M 作用。線上環境內存使用量在 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 生產環境參數配置,僅供參考請根據自己應用情況選擇性使用:

  • -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

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

2019-11-05 08:24:34

JavaOOM快速定位

2023-07-04 07:30:03

容器Pod組件

2020-07-08 09:50:37

Java內存快速定位

2025-11-17 07:48:48

2018-07-12 10:33:50

Docker容器內存

2022-06-01 09:38:36

KubernetesPod容器

2017-08-21 23:50:45

線上內存OOM

2024-12-06 08:00:00

K8s

2021-07-07 09:23:11

Java應用遷移

2025-04-28 08:45:16

2023-12-26 15:05:00

Linux共享內存配置

2023-10-10 15:26:30

內存泄露OOM

2025-03-20 08:49:01

2017-08-14 16:36:23

ASActivity內存

2022-01-02 08:42:50

架構部署容器

2022-01-27 08:27:23

Dubbo上下線設計

2024-03-18 15:44:48

K8S故障運維

2017-08-25 14:46:43

OOMBitmap內存

2025-10-28 01:10:00

2023-02-27 07:40:00

點贊
收藏

51CTO技術棧公眾號

亚洲欧美日韩一区二区三区在线| 一区二区高清免费观看影视大全 | 娇妻被老王脔到高潮失禁视频| 周于希免费高清在线观看| 国产欧美日韩在线视频| 国产在线观看一区二区三区| 久草视频中文在线| 精品国产91| 日韩精品一区二区三区在线| 91黄色小网站| 日本在线观看高清完整版| 国产在线观看av| 久久国产88| 精品国产一区二区三区久久| 中国免费黄色片| 福利一区二区三区视频在线观看| 亚洲欧美色图小说| 久久久久久99| 国产黄色美女视频| 久久精品在线| 色综合视频网站| 1024手机在线观看你懂的| 日本亚洲视频| 欧美午夜影院一区| 男人日女人视频网站| 亚洲搞黄视频| 久久综合999| 福利视频一区二区三区| 亚洲精品国产精品国自产网站按摩| 久久久久久美女精品| 亚洲视频一区二区三区| 欧美xxxxx少妇| 亚洲青青久久| 91国内精品野花午夜精品| 阿v天堂2018| 浪潮av一区| 日本一区二区三区四区| 精品国产乱码久久久久| 国产99久一区二区三区a片| 蜜臀va亚洲va欧美va天堂| 69久久夜色精品国产7777| 一级黄色录像视频| 色小子综合网| 三级精品视频久久久久| 四虎国产精品成人免费入口| 亚洲小说图片| 亚洲精品一区二区在线| 狠狠干一区二区| 国产免费黄色片| 免费在线观看精品| 国产精品欧美一区二区| 婷婷激情五月综合| 久久影院亚洲| 日韩免费在线看| 一本一道无码中文字幕精品热| 欧美性色综合| 国产69精品久久久| 日韩av电影网址| 一本久久综合| 日本a级片电影一区二区| 午夜精品三级久久久有码| 在线欧美一区| 8090成年在线看片午夜| 国产成人精品片| 国产欧美一区二区三区国产幕精品| 欧美激情精品久久久久久蜜臀 | 亚洲视频在线免费| 久久韩国免费视频| 欧美做爰爽爽爽爽爽爽| 激情综合自拍| 91大神福利视频在线| 国产www在线| 亚洲欧美网站| 国产精品一区二区久久国产| 91久久久久国产一区二区| 国产一区欧美日韩| 国产成人精品免费视频大全最热| 老牛影视av牛牛影视av| 99久久99久久精品免费观看| 欧美日韩精品免费看| 国产经典自拍视频在线观看| 国产精品美日韩| 国产香蕉一区二区三区| 手机av在线播放| 亚洲18色成人| 性欧美极品xxxx欧美一区二区| 8av国产精品爽爽ⅴa在线观看| 欧美乱妇15p| 久久久久亚洲av成人网人人软件| 欧美尿孔扩张虐视频| 国产亚洲激情在线| 欧美成人精品欧美一级私黄| 夜夜嗨av一区二区三区网站四季av| 国产99视频精品免视看7| 亚洲影院一区二区三区| 国产精品综合久久| 欧美国产二区| а√天堂在线官网| 欧美性极品xxxx做受| 在线看的黄色网址| youjizz亚洲| 尤物九九久久国产精品的特点| 免费高清在线观看电视| 野花国产精品入口| 91在线精品视频| 亚洲人成色777777老人头| 中文字幕中文乱码欧美一区二区| 天堂8在线天堂资源bt| 台湾佬中文娱乐久久久| 日韩美女在线视频| 天天干天天舔天天操| 国产综合网站| 国产日韩精品入口| 无码精品人妻一区二区| 亚洲人吸女人奶水| 亚洲成熟丰满熟妇高潮xxxxx| 国产一区二区三区国产精品| 亚洲三级 欧美三级| 久久网中文字幕| 免费成人你懂的| 精品国产乱码久久久久久丨区2区| 午夜视频成人| 色香蕉久久蜜桃| caopor在线| 91综合视频| 国产成人极品视频| 三级视频在线看| 亚洲免费高清视频在线| www.天天射.com| 亚洲精品无吗| 性亚洲最疯狂xxxx高清| 精品毛片一区二区三区| 中文字幕中文字幕一区| 五月婷婷深爱五月| 精品一区毛片| 3344国产精品免费看| 丰满大乳国产精品| 一区二区三区在线视频观看| 国产女同无遮挡互慰高潮91| 欧美艳星介绍134位艳星| 欧美性资源免费| 免费观看毛片网站| 一区二区三区 在线观看视频| 亚洲三级在线观看视频| 欧美xxxx中国| 国产一区二区丝袜| 男人天堂手机在线| 欧美日韩高清一区| 亚洲女人毛茸茸高潮| 蜜臀va亚洲va欧美va天堂| 婷婷亚洲婷婷综合色香五月| 日韩精品影片| 亚洲全黄一级网站| 伊人久久久久久久久久久久| 国产欧美日韩不卡| 网站一区二区三区| 日韩成人综合| 国产欧亚日韩视频| 久久日韩视频| 日韩欧美一区二区视频| 久久精品波多野结衣| 粉嫩绯色av一区二区在线观看| 屁屁影院ccyy国产第一页| 极品尤物一区| 欧美专区在线观看| 深夜福利在线观看直播| 欧美日韩一区二区免费在线观看| 搡老熟女老女人一区二区| 美女诱惑一区| 日韩精品久久一区| 亚洲国产综合在线观看| 不卡中文字幕av| 亚洲女人18毛片水真多| 欧美日韩一区二区精品| 免费看黄色三级| 韩日av一区二区| 波多野结衣与黑人| 欧美一级色片| 国产精品久久久久一区二区| 欧美激情午夜| 精品嫩草影院久久| 国产黄色免费观看| 国产精品九色蝌蚪自拍| 永久看看免费大片| 亚洲欧美日本视频在线观看| 神马影院一区二区| 狂野欧美xxxx韩国少妇| 91精品国产高清久久久久久91| 免费av在线电影| 在线播放日韩导航| 亚洲黄色一区二区| 中文字幕精品三区| 无码人妻精品一区二区三| 日韩经典中文字幕一区| 国产精品视频一二三四区| 蜜桃tv一区二区三区| 91免费视频国产| 色偷偷色偷偷色偷偷在线视频| 在线观看日韩视频| 亚洲经典一区二区三区| 色拍拍在线精品视频8848| 粉嫩av性色av蜜臀av网站| gogogo免费视频观看亚洲一| 不卡的av中文字幕| 一本久道久久综合婷婷鲸鱼| 在线观看日本一区| 亚洲区小说区| 国产精品对白刺激久久久| 影音成人av| 亚州成人av在线| 欧美成人hd| 亚洲欧美激情视频| 亚洲AV无码精品自拍| 欧美制服丝袜第一页| 国产精品1000| 亚洲情趣在线观看| mm131丰满少妇人体欣赏图| 国产成人亚洲综合色影视| 亚洲 欧美 另类人妖| 99亚洲一区二区| 欧美高清中文字幕| 午夜激情久久| 日韩久久不卡| 亚洲97av| 精品久久久久久乱码天堂| 午夜电影一区| 91久久综合亚洲鲁鲁五月天| 日韩大尺度黄色| 91精品国产色综合久久不卡98| av文字幕在线观看| 中文字幕国产日韩| 经典三级在线| 亚洲精品一区中文字幕乱码| а√中文在线资源库| 欧美精选一区二区| 最新黄色网址在线观看| 日本精品视频一区二区| 久久黄色精品视频| 天天操天天干天天综合网| 久久精品波多野结衣| 一区二区三区日韩精品视频| 中文字幕在线2021| 日韩美女视频一区二区| 久久久久人妻一区精品色| 国产视频不卡一区| 一区二区精品免费| 久久精品综合网| 成人精品999| 久久久久久综合| 日本一区二区三区网站| 91丨九色丨蝌蚪富婆spa| 亚洲欧美日韩偷拍| 91网站黄www| 玖玖爱在线观看| 亚洲高清久久| 国产 欧美 日本| 国产精品a级| 日韩黄色短视频| 日韩视频一区| 国产a级一级片| 日日噜噜夜夜狠狠视频欧美人| 精品国产成人av在线免| 老司机久久99久久精品播放免费| 亚洲乱码国产一区三区| 男人的j进女人的j一区| 日本中文字幕观看| 国产寡妇亲子伦一区二区| 中文字幕永久免费| 97精品久久久午夜一区二区三区| 大地资源二中文在线影视观看 | 丝袜美腿亚洲一区二区图片| 美女啪啪无遮挡免费久久网站| 国产美女福利视频| 亚洲欧美偷拍三级| 国产一级黄色av| 欧美日韩国产限制| 69视频免费看| 91精品国产91久久综合桃花| 国产黄色片免费观看| 亚洲国产精品999| 美丽的姑娘在线观看免费动漫| 一个色综合导航| 中文字幕在线观看网站| 91地址最新发布| 福利视频一区| 国产欧美韩日| 欧美影院三区| 黄色片免费在线观看视频| 妖精视频成人观看www| 色婷婷成人在线| 成人综合激情网| 黄色片网站免费| 亚洲乱码国产乱码精品精可以看| 日韩aaaaaa| 欧美剧在线免费观看网站| 亚洲卡一卡二卡三| 中文字幕日韩高清| 波多一区二区| 国产精品在线看| 欧洲精品一区| 精品国产一区二区三区在线| 午夜一级在线看亚洲| 午夜免费一级片| 久久综合色之久久综合| 国内偷拍精品视频| 在线欧美日韩国产| 狠狠人妻久久久久久综合麻豆| 在线国产精品播放| av今日在线| 91夜夜未满十八勿入爽爽影院| 国产欧美久久一区二区三区| 日本五级黄色片| 精品一区二区综合| 三上悠亚ssⅰn939无码播放| 亚洲综合色婷婷| 国产原创中文av| 尤物yw午夜国产精品视频| 日韩精品美女| 福利视频久久| 欧美.www| 欧美一级xxxx| 亚洲国产高清在线| 国产精品人人人人| 亚洲精品一区二区三区影院| 国产激情视频在线| 国产精品视频网址| 国产成人精品免费视| 男女激情无遮挡| 成人美女在线观看| 黑人巨大精品一区二区在线| 欧美日韩在线观看一区二区 | 久久草在线视频| 天天做天天躁天天躁| 狠狠网亚洲精品| 国产极品视频在线观看| 色拍拍在线精品视频8848| 色中色在线视频| 日本亚洲欧洲色α| 亚洲精品国产动漫| wwwxxx黄色片| 久久久久一区二区三区四区| 色av性av丰满av| 亚洲男子天堂网| 日韩三级影视| 日本精品一区| 七七婷婷婷婷精品国产| 2019男人天堂| 欧美三日本三级三级在线播放| 国内在线精品| 国产精品一香蕉国产线看观看| 欧美日韩中文一区二区| 亚洲精品视频导航| 国产精品三级av在线播放| 中文人妻熟女乱又乱精品| 中文字幕日韩av电影| 亚洲成人毛片| 欧美做暖暖视频| 成人福利视频在线看| 成年人免费高清视频| 亚洲毛片在线免费观看| 人人鲁人人莫人人爱精品| 亚洲高清视频一区| 国产综合色精品一区二区三区| 国产黄色的视频| 亚洲福利视频在线| 欧美momandson| 亚洲国产精品久久久久婷婷老年 | 成人9ⅰ免费影视网站| 国内精品美女在线观看| 国产美女视频免费观看下载软件| 欧美午夜女人视频在线| 91成人在线观看喷潮蘑菇| 久久青草国产手机看片福利盒子 | a资源在线观看| 91.com在线观看| 搞黄网站在线看| 日本精品一区二区三区不卡无字幕| 免费观看久久久4p| 欧美三级黄色大片| 亚洲福利在线看| 亚洲精品.com| 蜜臀在线免费观看| 久久综合九色综合97婷婷| 96亚洲精品久久久蜜桃| 欧美精品aaa| 加勒比久久综合| 人妻精油按摩bd高清中文字幕| 五月激情六月综合| www.亚洲.com| av资源站久久亚洲| 丝瓜av网站精品一区二区 | 国产在线观看网站| 91精品久久久久久久久久| 伊人久久亚洲热| 久久日免费视频| 亚洲第一国产精品| 精品176极品一区| 国产老熟妇精品观看| 18成人在线观看| 亚洲 美腿 欧美 偷拍|