亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

jvm堆外內存排查圖文舉例詳解

 更新時間:2023年12月15日 09:27:02   作者:Xd聊架構  
Java應用程序通過直接方式從操作系統(tǒng)中申請的內存,叫堆外內存,這篇文章主要給大家介紹了關于jvm堆外內存排查的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

前言

內存泄漏想必大家并不陌生,對于jvm的內存泄漏,有很多排查手段和方便的排查工具,例如MAL,但是對于非jvm的內存,如直接內存的使用,排查起來較為麻煩,下面介紹一下相關的排查手段

一、堆外內存排查

1.背景

在一次內存檢查的過程中,意外發(fā)現(xiàn)在linux的java進程內存占用,遠高于jvm的內存設定最大值(堆+非堆),第一時間是考慮java可以采用直接內存,如mmap對內存進行使用,但經過排查,發(fā)現(xiàn)并非如此,下面看一下排查過程

2.內存對比

首先通過top,可以看到java進行使用了4.2g的內存,且pid為2730

ps -ef|grep java
top -pid 2730

因為我知道我的jvm最大內存設置為3G左右,所以第一時間就沒有去看JVM的最大值,如果不確定,優(yōu)先查看JVM的堆、非堆的內存分配情況,本次忽略,直接查看NMT。

為了觀察java進程堆外內存的占用,JVM啟動參數(shù)中添加參數(shù):-XX:NativeMemoryTracking=summary,這個參數(shù)對jvm可能會有5%左右的性能損耗,所以生產環(huán)境不推薦開啟。

同時,-XX:+DisableExplicitGC:禁止顯示GC,即代碼中聲明的 System.gc();//建議jvm進行gc 不再生效。在jdk源碼中使用nio申請堆外內存時,堆外內存不足時會執(zhí)行 System.gc() 進行堆外內存的回收,所以,堆外內存使用較多時不推薦配置 -XX:+DisableExplicitGC。

最后,為了防止堆外內存的溢出,jvm啟動參數(shù)可以換添加:-XX:MaxDirectMemorySize=1024M

開啟NMT后,通過jcmd命令查看內存情況

jcmd 2730 VM.native_memory scale=MB

Heap(堆)、Class(元空間)、Thread(java線程棧,含GC本地線程)、Code(本地字節(jié)碼,即JIT存儲熱點代碼地方)、GC(JVM GC額外占用的,例如G1中的Remembered Set等數(shù)據結構)、Internal(Direct Buffer直接內存,例如nio)等占用。Native Memory Tracking表示該功能自身占用的部分。

JVM 的內存大致分為下面這幾個部分:

  • 堆(Heap):young、old 區(qū)域等
  • 線程棧(Thread Stack):每個線程棧預留 1M 的線程棧大小
  • 非堆(Non-heap):包括 code_cache、metaspace 等
  • 堆外內存:unsafe.allocateMemory 和 DirectByteBuffer 申請的堆外內存
  • native (C/C++ 代碼)申請的內存
  • 還有 JVM 運行本身需要的內存,比如 GC 等

可以看到JVM只使用了3G左右,其中Internal的39M為直接內存的使用,那么剩余的1.2G非JVM的使用。因為jcmd命令顯示的內存包含堆內內存、Code區(qū)域、通過unsafe.allocateMemory和DirectByteBuffer申請的內存,但是不包含其他Native Code(C代碼)申請的堆外內存。所以猜測是使用Native Code申請內存所導致的問題。

其他jcmd可用命令:

查看java進程內存占用詳細情況(-XX:NativeMemoryTracking=summary,關閉NMT命令:jcmd pid VM.native_memory shutdown):
jcmd pid VM.native_memory scale=MB
 
保存java進程內存占用情況的基準版本:
jcmd pid VM.native_memory scale=MB baseline
 
與基準版本進行比較(若懷疑存在內存泄漏,可過段時間再執(zhí)行觀察):
jcmd pid VM.native_memory scale=MB summary.diff

3.堆外內存檢查

通過pmap打印內存的分布情況,并從打到小排序

pmap 2730 -x | sort -k 3 -n -r > /tmp/pmap20230131

打印jcmd內存使用明細

jcmd 2730 VM.native_memory detail scale=MB > /tmp/jcmd20230131.txt

在pmap文件中,發(fā)現(xiàn)大量的64M的地址;而這些地址空間不在jcmd命令所給出的地址空間里面(例如通過pmap其中一個內存地址7f6f90000000,去jcmd明細中搜索,無法搜到即為沒有在jvm中有引用),基本上就斷定就是這些64M的內存所導致。

4.排查堆外內存

因猜測是Native Code所引起,Java層面的工具不便于排查此類問題,只能使用系統(tǒng)層面的工具去定位問題。

1.使用smaps查看內存的起始地址

cat /proc/2730/smaps > /tmp/smaps20230131.txt

以上述7f6f90000000地址為例,因在jcmd中無法找到7f6f90000000地址的內容,說明非jvm內存,在smaps文件中搜索7f6f90000000的起始地址。

2.使用gdb調試工具打印上述懷疑的內存地址里面存儲的內容(注:gdb從進入到退出的中間時刻,會使java進程無法訪問,處于掛起狀態(tài),生產環(huán)境小心使用)。

#進入gdb
gdb -pid 2730
#打印內存地址地址內容(0x00007f6f90000000 0x00007f6f93fff000)為開始地址和結束地址
dump memory mem.bin 0x00007f6f90000000 0x00007f6f93fff000
#退出
quit
#將文本以字符串輸出
strings mem.bin > /tmp/mem20230131.txt

因進入gdb會導致進程無法訪問,建議使用下面的方式執(zhí)行命令

#0x00007f6f90000000 可以寫成0x7f6f90000000 
gdb --batch --pid 2730-ex "dump memory /tmp/ipo02061143.bin 0x7f6f90000000 0x7f6f93fff000"

可以看看里面有一些報錯和具體的調用方法等,根據內存里面的內容進行逐一檢查即可

5.glibc內存泄露

像上述的問題,內存內容里面沒有可疑點,并且pmap打印的內容中有大量的64M內存區(qū)域,由此可發(fā)現(xiàn),這是linux經典的glibc內存泄露問題,后續(xù)會專門寫一篇文章介紹linux內存管理以及glibc相關的原理,這里先直接說明結論。

原因:

glibc 的內存分配策略導致的碎片化內存回收問題,導致看起來像是內存泄露,那有沒有更好一點的對碎片化內存的 malloc 庫呢?業(yè)界常見的有 google 家的 tcmalloc 和 facebook 家的 jemalloc。

tcmalloc

#安裝
yum install gperftools-libs.x86_64 
#使用 LD_PRELOAD 掛載
export LD_PRELOAD="/usr/lib64/libtcmalloc.so.4.4.5"

注意 java 應用要重啟

jemalloc

#安裝
yum install epel-release  -y
yum install jemalloc -y
#使用 LD_PRELOAD 掛載
export LD_PRELOAD="/usr/lib64/libjemalloc.so.1"

使用 jemalloc 后,RSS 內存呈周期性波動,波動范圍約 2 個百分點以內,基本控制住了

總結

到此這篇關于jvm堆外內存排查的文章就介紹到這了,更多相關jvm堆外內存排查內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論