衡阳派盒市场营销有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

介紹一下使用NMT協助排查內存問題的案例

openEuler ? 來源:畢昇編譯 ? 作者:竇義望 ? 2022-11-16 11:33 ? 次閱讀

從前面幾篇文章,我們了解了 NMT 的基礎知識以及 NMT 追蹤區域分析的相關內容,本篇文章將為大家介紹一下使用 NMT 協助排查內存問題的案例。

6.使用 NMT 協助排查內存問題案例

我們在搞清楚 NMT 追蹤的 JVM 各部分的內存分配之后,就可以比較輕松的協助排查定位內存問題或者調整合適的參數

可以在 JVM 運行時使用 jcmd VM.native_memory baseline 創建基線,經過一段時間的運行后再使用 jcmd VM.native_memory summary.diff/detail.diff 命令,就可以很直觀地觀察出這段時間 JVM 進程使用的內存一共增長了多少,各部分使用的內存分別增長了多少,可以很方便的將問題定位到某一具體的區域。

比如我們看到 MetaSpace 的內存增長異常,可以結合 MAT 等工具查看是否類加載器數量異常、是否類重復加載、reflect 的 inflation 參數設置是否合理;如果 Symbol 內存增長異常,可以查看項目 String.intern 是否使用正常;如果 Thread 使用內存過多,考慮是否可以適當調整線程堆棧大小等等。

案例一:虛高的 VIRT 內存

我們還記得前文(NMT 內存 & OS 內存概念差異性章節)中使用 top 命令查看啟動的 JVM 進程,仔細觀察會發現一個比較虛高的 VIRT 內存(10.7g),我們使用 NMT 追蹤的 Total: reserved 才 2813709KB(2.7g),這多出來的這么多虛擬內存是從何而來呢?

top

PIDUSERPRNIVIRTRESSHRS%CPU%MEMTIME+COMMAND
27420douyiwa+20010.7g69756017596S100.00.30:18.79java

NativeMemoryTracking:

Total:reserved=2813077KB,committed=1496981KB

使用pmap -X 觀察內存情況:

27420:java-Xmx1G-Xms1G-XX:+UseG1GC-XX:MaxMetaspaceSize=256M-XX:MaxDirectMemorySize=256M-XX:ReservedCodeCacheSize=256M-XX:NativeMemoryTracking=detail-jarnmtTest.jar
AddressPermOffsetDeviceInodeSizeRssPssReferencedAnonymousLazyFreeShmemPmdMappedShared_HugetlbPrivate_HugetlbSwapSwapPssLockedMapping
c0000000rw-p0000000000:00010490886372366372366372366372360000000
100080000---p0000000000:000104806400000000000
aaaaea835000r-xp00000000fd:0245613083444400000000java
aaaaea854000r--p0000f000fd:0245613083444440000000java
aaaaea855000rw-p00010000fd:0245613083444440000000java
aaab071af000rw-p0000000000:0003041081081081080000000[heap]
fffd60000000rw-p0000000000:00013244440000000
fffd60021000---p0000000000:0006540400000000000
fffd68000000rw-p0000000000:00013288880000000
fffd68021000---p0000000000:0006540400000000000
fffd6c000000rw-p0000000000:00013244440000000
fffd6c021000---p0000000000:0006540400000000000
fffd70000000rw-p0000000000:000132404040400000000
fffd70021000---p0000000000:0006540400000
......

可以發現多了很多 65404 KB 的內存塊(大約 120 個),使用 /proc//smaps 觀察內存地址:

......
fffd60021000-fffd64000000---p0000000000:000
Size:65404kB
KernelPageSize:4kB
MMUPageSize:4kB
Rss:0kB
Pss:0kB
Shared_Clean:0kB
Shared_Dirty:0kB
Private_Clean:0kB
Private_Dirty:0kB
Referenced:0kB
Anonymous:0kB
LazyFree:0kB
AnonHugePages:0kB
ShmemPmdMapped:0kB
Shared_Hugetlb:0kB
Private_Hugetlb:0kB
Swap:0kB
SwapPss:0kB
Locked:0kB
VmFlags:mrmwmenr
......

對照 NMT 的情況,我們發現如 fffd60021000-fffd64000000 這種 65404 KB 的內存是并沒有被 NMT 追蹤到的。這是因為在 JVM 進程中,除了 JVM 進程自己 mmap 的內存(如 Java Heap,和用戶進程空間的 Heap 并不是一個概念)外,JVM 還直接使用了類庫的函數來分配一些數據,如使用 Glibc 的 malloc/free (也是通過 brk/mmap 的方式):

6456547a-64fa-11ed-8abf-dac502259ad0.png

既然 JVM 使用了 Glibc 的 malloc/free,就不得不提及 malloc 的機制,早期版本的 malloc 只有一個 arena(分配區),每次分配時都要對分配區加鎖,分配完成之后再釋放,這就導致了多線程的情況下競爭比較激烈。

所以 malloc 改動了其分配機制,甚至有了 arena per-thread 的模式,即如果在一個線程中首次調用 malloc,則創建一個新的 arena,而不是去查看前面的鎖是否會發生競爭,對于一定數量的線程可以避免競爭在自己的 arena 上工作。

arena 的數量限制在 32 位系統上是 2 * CPU 核心數,64 位系統上是 8 * CPU 核心數,當然我們也可以使用 MALLOC_ARENA_MAX (Linux 環境變量,詳情可以查看 mallopt(3)[1])來控制。

查看發現運行 JVM 進程的環境 CPU 信息(物理 CPU 核數):Core(s) per socket: 64 。

我們給當前環境設置 MALLOC_ARENA_MAX=2,重啟 JVM 進程,查看使用情況:

top

PIDUSERPRNIVIRTRESSHRS%CPU%MEMTIME+COMMAND
36319douyiwa+200310834069087217828S100.00.30:07.61java

虛高的 VIRT 內存已經降下來了,繼續查看 pmap/smaps 會發現眾多的 65404 KB 的內存空間也消失了(120 * 65404 KB = 7848480 KB 正好對應了 10.7g - 3108340 KB 的內存,即 VIRT 降低的內存)。

為什么我們的 JVM 進程會使用如此多的 arena 呢?因為我們在啟動 JVM 進程的時候,并沒有手動去設置一些進程的數目,如:CICompilerCount(編譯線程數)、ConcGCThreads/ParallelGCThreads(并發 GC 線程數)、G1ConcRefinementThreads(G1 Refine 線程數)等等。

這些參數大多數根據當前機器的 CPU 核數去計算默認值,使用 jinfo -flags 查看機器參數發現:

-XX:CICompilerCount=18
-XX:ConcGCThreads=11
-XX:G1ConcRefinementThreads=43

這些線程數目都是比較大的,我們也可以不修改 MALLOC_ARENA_MAX 的數量,而通過參數減小線程的數量來減少 arena 的數量。

Glibc 的 malloc 有時會出現碎片問題,可以使用 jemalloc/tcmalloc 等替代 Glibc。

案例二:堆外內存的排查

有時候我們會發現,Java 堆、MetaSpace 等區域是比較正常的,但是 JVM 進程整體的內存卻在不停的增長,此時我們就可以使用 NMT 的 baseline & diff 功能來觀察究竟是哪塊區域內存一直增長。

比如在一次案例中發現:

NativeMemoryTracking:
Total:reserved=8149334KB+1535794KB,committed=6999194KB+1590490KB
......
-Internal(reserved=1723321KB+1472458KB,committed=1723321KB+1472458KB)
(malloc=1723289KB+1472458KB#109094+47573)
(mmap:reserved=32KB,committed=32KB)
......
[0x00007fceb806607a]Unsafe_AllocateMemory+0x17a
[0x00007fcea1d24e68]
(malloc=1485579KBtype=Internal+1455929KB#2511+2277)
......

我們可以確認內存 1590490KB 的增長,基本上都是由 Internal 的 Unsafe_AllocateMemory 所分配的,此時可以優先考慮 NIO 中 ByteBuffer.allocateDirect / DirectByteBuffer / FileChannel.map 等使用方式是不是出現了泄漏,可以使用 MAT 查看 DirectByteBuffer 對象的數量是否異常,并可以使用 -XX:MaxDirectMemorySize 來限制 Direct 的大小。

設置 -XX:MaxDirectMemorySize 之后,進程異常的內存增長停止,但是 GC 頻率變高,查看 GC 日志發現:.887+0800: 238210.127: [Full GC (System.gc()) 1175M->255M(3878),0.8370418 secs]。

FullGC 的頻率大大增加,并且基本上都是由 System.gc() 顯式調用引起的(HotSpot中的System.gc()為 FulGC),查看 DirectByteBuffer 相關邏輯:

#DirectByteBuffer.java

DirectByteBuffer(intcap){//package-private
......
Bits.reserveMemory(size,cap);

longbase=0;
try{
base=unsafe.allocateMemory(size);
}catch(OutOfMemoryErrorx){
Bits.unreserveMemory(size,cap);
throwx;
}
unsafe.setMemory(base,size,(byte)0);
if(pa&&(base%ps!=0)){
//Rounduptopageboundary
address=base+ps-(base&(ps-1));
}else{
address=base;
}
cleaner=Cleaner.create(this,newDeallocator(base,size,cap));
att=null;
}

#Bits.java

staticvoidreserveMemory(longsize,intcap){
......
System.gc();
......
}

DirectByteBuffer 在 unsafe.allocateMemory(size) 之前會先去做一個 Bits.reserveMemory(size, cap) 的操作,Bits.reserveMemory 會顯式的調用 System.gc() 來嘗試回收內存,看到這里基本可以確認為 DirectByteBuffer 的問題,排查業務代碼,果然發現一處 ByteBufferStream 使用了 ByteBuffer.allocateDirect 的方式而流一直未關閉釋放內存,修正后內存增長與 GC 頻率皆恢復正常。






審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • JVM
    JVM
    +關注

    關注

    0

    文章

    158

    瀏覽量

    12261
  • LINUX內核
    +關注

    關注

    1

    文章

    316

    瀏覽量

    21744
  • NMT
    NMT
    +關注

    關注

    0

    文章

    7

    瀏覽量

    3654

原文標題:Native Memory Tracking 詳解(4):使用 NMT 協助排查內存問題案例

文章出處:【微信號:openEulercommunity,微信公眾號:openEuler】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    詳細介紹一下PSS+Pnoise仿真

    PSS+Pnoise仿真是很多電路要用到的仿真,今天我們詳細介紹一下這個仿真。
    的頭像 發表于 11-03 18:13 ?8378次閱讀
    詳細<b class='flag-5'>介紹</b><b class='flag-5'>一下</b>PSS+Pnoise仿真

    RTOS內存管理問題誰來解答一下

    定義了個buff,通過pvPortMalloc函數給buff申請了內存。但是如何去判斷我申請到的BUFF的內存有沒有用完呢?看來一下介紹
    發表于 06-15 04:35

    請問一下示波器可用于EMI排查嗎?

    請問一下示波器可用于EMI排查嗎?
    發表于 04-30 06:48

    請問一下示波器可用于電磁干擾排查嘛?

    工程師在排查EMI問題所面臨的最常見的挑戰是什么?如何區分出無用的騷擾行為?請問一下示波器可用于電磁干擾排查嘛?
    發表于 05-08 07:40

    分享內存泄漏定位排查技巧

    的調試工具,下面分享內存泄漏定位排查技巧。1.對malloc,free進行封裝首先,我們對malloc,f
    發表于 12-17 08:13

    Native Memory Tracking 詳解(4):使用 NMT 協助排查內存問題案例

    從前面幾篇文章,我們了解了 NMT 的基礎知識以及 NMT 追蹤區域分析的相關內容,本篇文章將為大家介紹一下使用 NMT
    發表于 11-24 14:19

    簡要介紹一下Python-UNO的使用方法

    OpenOffice是個免費的、開源的辦公套裝,集成了允許開發者用不同語言進行開發的API。Python-UNO讓你可以在Python環境使用OpenOffice。本文簡要介紹一下
    的頭像 發表于 01-04 14:54 ?8717次閱讀
    簡要<b class='flag-5'>介紹</b><b class='flag-5'>一下</b>Python-UNO的使用方法

    電磁爐加熱一下就停一下什么原因及解決辦法

    電磁爐有時會出現加熱故障,現象是熱一下一下在熱一下又停一下,基本隔
    發表于 03-18 09:02 ?27.6w次閱讀

    電磁爐加熱一下就停一下什么原因

    電磁爐加熱一下就停一下什么原因。
    的頭像 發表于 06-04 10:01 ?3.9w次閱讀

    如何使用NMT和pmap來解決JVM的資源泄漏問題

    編者按:筆者使用 JDK 自帶的內存跟蹤工具 NMT 和 Linux 自帶的 pmap 解決了個非常典型的資源泄漏問題。這個資源泄漏是由于 Java 程序員不正確地使用 Java API 導致
    的頭像 發表于 09-24 16:00 ?3588次閱讀
    如何使用<b class='flag-5'>NMT</b>和pmap來解決JVM的資源泄漏問題

    介紹NMT追蹤區域的部分內存類型

    除去這上面的部分選項,我們發現 NMT 中還有個 unknown 選項,這主要是在執行 jcmd 命令時,內存類別還無法確定或虛擬類型信息還沒有到達時的
    的頭像 發表于 10-21 09:26 ?1252次閱讀

    介紹一下高低溫試驗箱的校驗項目與方法

    介紹一下高低溫試驗箱的校驗項目與方法
    的頭像 發表于 06-12 09:49 ?507次閱讀
    <b class='flag-5'>介紹</b><b class='flag-5'>一下</b>高低溫試驗箱的校驗項目與方法

    次Rust內存泄漏排查之旅

    在某次持續壓測過程中,我們發現 GreptimeDB 的 Frontend 節點內存即使在請求量平穩的階段也在持續上漲,直至被 OOM kill。我們判斷 Frontend 應該是有內存泄漏了,于是開啟了排查
    的頭像 發表于 07-02 11:52 ?716次閱讀
    記<b class='flag-5'>一</b>次Rust<b class='flag-5'>內存</b>泄漏<b class='flag-5'>排查</b>之旅

    glibc導致的堆外內存泄露的排查過程

    本文記錄次glibc導致的堆外內存泄露的排查過程。
    的頭像 發表于 09-01 09:43 ?764次閱讀
    glibc導致的堆外<b class='flag-5'>內存</b>泄露的<b class='flag-5'>排查</b>過程

    java內存溢出排查方法

    過程中常見的問題之,可能導致應用程序崩潰、性能下降甚至系統崩潰。在本文中,將詳細介紹如何排查和解決Java內存溢出問題。 、什么是Jav
    的頭像 發表于 11-23 14:46 ?3342次閱讀
    百家乐平注法到65| 百家乐揽子打法| 金城百家乐玩法| 百家乐官网技巧玩法技巧| 澳门百家乐代理| 真人百家乐蓝盾娱乐网| 百家乐游戏打水方法| 百家乐输惨了| 肯博百家乐现金网| 百家乐娱乐平台官网网| 大发888如何注册送58| 成武县| E乐博百家乐官网现金网| 百乐门娱乐城注册| 华坪县| 百家乐官网稳赢秘诀教学| 赌博百家乐有技巧吗| 吉利百家乐的玩法技巧和规则| 总统娱乐城能赢钱吗| 百家乐官网出租平台| 百家乐官网赌场策略大全| 在线百家乐| 大发888bet娱乐城| 百家乐官网比赛技巧| 韩国百家乐官网的玩法技巧和规则| 百家乐干洗店| 德州扑克游戏下载| 天镇县| 免水百家乐官网的玩法技巧和规则 | 南投县| 百家乐官网平台注册送现金| 百家乐官网用品| 百家乐免费注册| 缅甸百家乐官网论坛| 杨公风水24山分金| 大发888游戏软件下载| 百家乐官网手机游戏下载| 百家乐游戏平台排名| 大发888官方lc8| 大佬百家乐官网现金网| 乐九百家乐现金网|