精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

亚洲资源在线播放| 一区二区三区免费播放| 国产精品九九九九| 欧美gay男男猛男无套| 欧美亚洲国产一卡| 视频在线一区二区三区| 日本一区二区三区久久| 久久日文中文字幕乱码| 欧美高清精品3d| 欧美日韩在线不卡一区| 无码人妻丰满熟妇奶水区码| 妖精视频一区二区三区免费观看| 一区二区三区毛片| 国产欧美一区二区视频 | 久久久久九九视频| 国产精品吹潮在线观看| 999精品久久久| 国产国产一区| 亚洲免费观看高清在线观看| 懂色一区二区三区av片| 久久免费黄色网址| 国产高清精品二区| 亚洲一区二区成人在线观看| 精品久久精品久久| 国产在线一级片| 91精品国产福利在线观看麻豆| 日韩亚洲国产中文字幕欧美| 国产一二三区在线播放| 亚洲国产精品二区| 久久亚洲一区| 久久综合久久88| 亚洲精品无码久久久久久久| 2021国产在线| 91一区在线观看| 国产女精品视频网站免费| 国产亚洲精品成人| 亚洲小说图片视频| 51午夜精品国产| 久久99久久久久久| 成人在线二区| 狠狠色丁香九九婷婷综合五月 | 在线免费看黄| 成人精品电影在线观看| 国产精品稀缺呦系列在线| 国产亚洲第一页| 日韩大片在线播放| 91精品国产黑色紧身裤美女| 免费毛片小视频| 国产成人无吗| 国产性做久久久久久| 99精品99久久久久久宅男| 日韩精品成人免费观看视频| 你懂的成人av| 中文字幕欧美国内| 国产一级二级在线观看| www.国产精品| 狠狠躁18三区二区一区| 97精品国产97久久久久久粉红| 欧美成人免费| 国产精品一区在线| 国产日韩在线亚洲字幕中文| 国产成人在线视频观看| 欧美日韩国产探花| 精品久久久999| 国产精久久一区二区三区| 风间由美一区二区av101 | 一区二区视频网站| 久久久人人人| 69久久夜色精品国产69| 久久97人妻无码一区二区三区| 加勒比久久综合| 亚洲精品国产suv| 尤物网站在线看| 免费成人黄色网| 在线观看一区二区视频| 黑人糟蹋人妻hd中文字幕| 69av成人| 亚洲美女区一区| 日本黄色播放器| 香蕉视频在线免费看| 亚洲熟女乱综合一区二区| japansex久久高清精品| 欧美二区乱c少妇| 久久国产激情视频| 欧美一区二区三区婷婷| 欧美日韩一区久久| 久久这里只精品| 成人精品动漫| 欧美日韩国产一级二级| 精品999在线| 成人在线高清| 91成人网在线| 天堂av在线网站| 福利一区二区免费视频| 欧美日韩中文字幕一区| 三上悠亚在线一区| 日韩一区精品| 欧美日韩日本视频| 成人性生交视频免费观看| 亚洲啊v在线免费视频| 精品国产一区二区三区忘忧草 | 欧美精品福利在线| 少妇久久久久久被弄高潮| 日韩毛片视频| 日韩在线观看高清| 青青草原在线免费观看视频| 亚洲午夜激情在线| 91精品国产高清自在线| 精品国产乱子伦| 蜜臀av一级做a爰片久久| 成人免费视频在线观看超级碰| 国产丝袜在线视频| 国产精品自拍网站| 国产精品v欧美精品∨日韩| 少妇av在线播放| 久久这里都是精品| 国产系列第一页| 91jq激情在线观看| 欧美视频日韩视频在线观看| 欧美成人手机在线视频| 久久a爱视频| 中文字幕不卡av| 国产这里有精品| 久久精品欧洲| 91久久久久久久久久久久久| 亚洲黄色在线观看视频| 久久精品亚洲国产奇米99| 中文字幕一区综合| 黑森林国产精品av| 欧美色综合久久| 日本久久久久久久久久| 国产91一区| 欧美另类xxx| 久草视频一区二区| 国产精品中文字幕日韩精品| 久久青青草综合| 欧美精品videos另类| 精品日韩视频在线观看| 成年人网站av| 欧美久久综合网| 久久久人成影片一区二区三区| 中文字幕一区二区三区四区欧美| 国产一区二区免费看| 欧美成人蜜桃| 成人影音在线| 制服丝袜av成人在线看| 99久久精品免费视频 | av动漫在线播放| 婷婷综合六月| 亚洲国产女人aaa毛片在线| 国产一区第一页| 亚洲欧美成人综合| caoporen国产精品| 国产激情小视频在线| 在线精品视频免费观看| 国产三级视频网站| 亚洲日本激情| 亚洲综合视频1区| 99精品老司机免费视频| 色综合久久中文字幕| 国产艳妇疯狂做爰视频 | 精品三级久久久久久久电影聊斋| 亚洲人吸女人奶水| 天堂av在线网站| 精品国产精品国产偷麻豆| 69**夜色精品国产69乱| 日批视频免费播放| 亚洲国产精品久久久久婷婷884 | 国产一区二区视频免费| 91网站最新网址| 精品视频在线观看一区| 成人涩涩网站| 久久久久成人网| 亚洲国产精品久久久久爰性色| 中文字幕一区二区三区在线观看| 91最新在线观看| 猛男gaygay欧美视频| 久久久久久久一区二区| www.xxx国产| 亚洲精品成人a在线观看| 天堂av手机在线| 91视频综合| 成人有码在线播放| 久草中文在线| 欧美一区二区免费视频| 紧身裙女教师波多野结衣| 精品一区二区免费视频| 一区二区精品在线观看| 91成人福利社区| 久久综合久中文字幕青草| 精品国自产在线观看| 一区二区三区四区亚洲| 国产精品熟妇一区二区三区四区| 亚洲三级毛片| 精品无人区一区二区三区竹菊| 深夜成人在线| 亚洲精品aⅴ中文字幕乱码| 日韩黄色一级大片| 久久久久久久久久电影| 狠狠热免费视频| av在线不卡免费观看| 国产精品福利在线| 很黄的网站在线观看| 欧美一卡二卡三卡四卡| 国产污片在线观看| 久久久噜噜噜久久中文字幕色伊伊| 国产一线二线三线在线观看| 羞羞色午夜精品一区二区三区| 91网站在线免费观看| 操喷在线视频| 亚洲男人天天操| 亚洲熟妇无码久久精品| 一区二区三区在线视频观看58| 国产精品入口麻豆| 日本一不卡视频| 男女啪啪的视频| 高清精品视频| 国产精品流白浆视频| 亚洲综合影视| 亚洲毛片一区二区| 91theporn国产在线观看| 一区二区三区精品久久久| 深爱五月激情网| 国产制服丝袜一区| 欧美在线观看成人| 亚洲电影在线一区二区三区| 久热国产精品视频一区二区三区| 国产成+人+综合+亚洲欧美| 欧美福利视频在线| 免费在线稳定资源站| 欧美在线观看禁18| 久久精品视频日本| 国产精品国产三级国产| 亚洲天堂网一区二区| 国产自产视频一区二区三区| 99999精品视频| 欧美+日本+国产+在线a∨观看| 女同一区二区| 白白在线精品| 成人黄色av网站| 英国三级经典在线观看| 久久久黄色av| 成人性爱视频在线观看| 精品国免费一区二区三区| 中文字幕乱码视频| 狠狠干狠狠久久| 免费中文字幕在线观看| 91香蕉国产在线观看软件| 无码人妻少妇色欲av一区二区| 日韩av在线免费观看不卡| 手机在线视频你懂的| 亚洲美女15p| 亚洲一区二区免费| 成人福利av| 久久久久久久久爱| 精品美女在线观看视频在线观看| 亚洲第一区第一页| 国产乱淫av片免费| 日本精品一级二级| 欧美日韩国产精品综合| 中文字幕va一区二区三区| 在线免费观看日韩av| 99久久亚洲一区二区三区青草| 狠狠操狠狠干视频| 欧美亚洲免费| 国产亚洲欧美在线视频| 伊人激情综合| 男人草女人视频| 欧美一区国产在线| 久久99国产精品一区| 一区二区三区四区电影| 亚洲一二区在线| 欧美人与牛zoz0性行为| 欧美色图亚洲自拍| 亚洲亚洲免费| 精品国产免费久久久久久尖叫 | 丁香婷婷综合激情五月色| 日韩欧美色视频| 国产成人a级片| 国产吃瓜黑料一区二区| 国产精品亚洲第一区在线暖暖韩国| 九一精品久久久| 狠狠色丁香久久婷婷综合_中| 免费黄频在线观看| 国产麻豆视频一区| 无码人妻一区二区三区免费n鬼沢 久久久无码人妻精品无码 | 日韩成年人视频| 亚洲国产精品影院| 国产十六处破外女视频| 成人欧美一区二区三区| 亚洲色图日韩精品| 中文字幕在线观看一区二区| 黄色精品视频在线观看| 中文字幕一区在线| 老熟妻内射精品一区| 亚洲精品国产视频| 五月天婷婷综合网| 在线一区二区三区| 国产麻豆91视频| 日韩免费观看高清完整版 | 亚洲精品成人无码毛片| 99精品久久久久久| 在线观看国产精品一区| 国产精品高潮呻吟| 久久久精品人妻一区二区三区四 | 538精品在线视频| 一区二区三区产品免费精品久久75| 青草草在线视频| 精品久久中文字幕| 97人妻精品视频一区| 91麻豆精品国产自产在线观看一区| www.com欧美| 亚洲另类激情图| 久久久久久国产精品免费无遮挡| 九色精品美女在线| 亚洲少妇视频| 国产一区视频在线播放| 成人香蕉社区| 色综合视频二区偷拍在线| 伊人久久大香线蕉综合四虎小说| av日韩一区二区三区| 日本免费在线视频不卡一不卡二| 伊人国产精品视频| 91在线视频网址| 天天躁日日躁aaaa视频| 综合分类小说区另类春色亚洲小说欧美| 婷婷色中文字幕| 91久久精品一区二区| 亚洲免费成人在线| 曰本色欧美视频在线| 成人午夜在线影视| 日韩暖暖在线视频| 青青在线精品| 黑人中文字幕一区二区三区| 精品国产一区探花在线观看| 第九区2中文字幕| 欧美综合国产| 极品白嫩少妇无套内谢| 国产精品视频免费| 男人日女人网站| 欧美不卡一区二区| 天堂资源在线中文| 欧美中文字幕视频在线观看| 国产美女亚洲精品7777| 欧美日韩大片一区二区三区| 欧美成人中文| 中文字幕 91| 久久久三级国产网站| 国产无遮无挡120秒| 51久久夜色精品国产麻豆| 国产51人人成人人人人爽色哟哟| 欧美激情免费视频| 国产精品视频首页| 亚洲欧美国产不卡| 一本色道88久久加勒比精品| 国产一级特黄a大片免费| 久久久亚洲高清| 国产成人免费观看视频 | 野花社区视频在线观看| 午夜天堂影视香蕉久久| 国产喷水福利在线视频| 色yeye香蕉凹凸一区二区av| 性高爱久久久久久久久| 国产欧美一区二区在线播放| 欧美色综合网| 一道本视频在线观看| 久久精品在线观看| 午夜婷婷在线观看| 亚洲精品一区在线观看| 蜜臀av在线| www.久久久| 欧美在线黄色| 性折磨bdsm欧美激情另类| 亚洲日本在线视频观看| 国产毛片久久久久| 久久色免费在线视频| 国产精品国产三级在线观看| 精品一区二区成人免费视频 | 欧美性生活久久| 国产高清视频在线播放| 国产精品久久久久久久久粉嫩av| 国产成人精品免费视| 国产精彩免费视频| 国产精品久久久久久久久免费丝袜| 国产精品51麻豆cm传媒| 中文字幕亚洲一区在线观看| 午夜av成人| 一本色道久久99精品综合| 国产尤物一区二区在线| 久久精品视频免费在线观看| 亚洲成人av片| 在线观看涩涩| 欧美最大成人综合网| 免费观看在线综合色| 四虎精品免费视频| 精品国产麻豆免费人成网站| 九色porny自拍视频在线播放| 免费不卡亚洲欧美| 亚洲黄页一区| 久久久久久久久久久国产精品|