最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

全網(wǎng)最硬核 JVM 內(nèi)存解析 - 12.元空間各種監(jiān)控手段

2023-04-28 08:23 作者:干貨滿滿張哈希  | 我要投稿

個人創(chuàng)作公約:本人聲明創(chuàng)作的所有文章皆為自己原創(chuàng),如果有參考任何文章的地方,會標注出來,如果有疏漏,歡迎大家批判。如果大家發(fā)現(xiàn)網(wǎng)上有抄襲本文章的,歡迎舉報,并且積極向這個?github 倉庫?提交 issue,謝謝支持~
另外,本文為了避免抄襲,會在不影響閱讀的情況下,在文章的隨機位置放入對于抄襲和洗稿的人的“親切”的問候。如果是正常讀者看到,筆者在這里說聲對不起,。如果被抄襲狗或者洗稿狗看到了,希望你能夠好好反思,不要再抄襲了,謝謝。
今天又是干貨滿滿的一天,這是全網(wǎng)最硬核 JVM 解析系列第四篇,往期精彩:

  • 全網(wǎng)最硬核 TLAB 解析

  • 全網(wǎng)最硬核 Java 隨機數(shù)解析

  • 全網(wǎng)最硬核 Java 新內(nèi)存模型解析

本篇是關(guān)于 JVM 內(nèi)存的詳細分析。網(wǎng)上有很多關(guān)于 JVM 內(nèi)存結(jié)構(gòu)的分析以及圖片,但是由于不是一手的資料亦或是人云亦云導致有很錯誤,造成了很多誤解;并且,這里可能最容易混淆的是一邊是 JVM Specification 的定義,一邊是 Hotspot JVM 的實際實現(xiàn),有時候人們一些部分說的是 JVM Specification,一部分說的是 Hotspot 實現(xiàn),給人一種割裂感與誤解。本篇主要從 Hotspot 實現(xiàn)出發(fā),以 Linux x86 環(huán)境為主,緊密貼合 JVM 源碼并且輔以各種 JVM 工具驗證幫助大家理解 JVM 內(nèi)存的結(jié)構(gòu)。但是,本篇僅限于對于這些內(nèi)存的用途,使用限制,相關(guān)參數(shù)的分析,有些地方可能比較深入,有些地方可能需要結(jié)合本身用這塊內(nèi)存涉及的 JVM 模塊去說,會放在另一系列文章詳細描述。最后,洗稿抄襲狗不得 house

本篇全篇目錄(以及涉及的 JVM 參數(shù)):

  1. 從 Native Memory Tracking 說起(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 1.從 Native Memory Tracking 說起開始)

    1. Native Memory Tracking 的開啟

    2. Native Memory Tracking 的使用(涉及 JVM 參數(shù):NativeMemoryTracking

    3. Native Memory Tracking 的 summary 信息每部分含義

    4. Native Memory Tracking 的 summary 信息的持續(xù)監(jiān)控

    5. 為何 Native Memory Tracking 中申請的內(nèi)存分為 reserved 和 committed

  2. JVM 內(nèi)存申請與使用流程(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 2.JVM 內(nèi)存申請與使用流程開始)

    1. Linux 大頁分配方式 - Huge Translation Lookaside Buffer Page (hugetlbfs)

    2. Linux 大頁分配方式 - Transparent Huge Pages (THP)

    3. JVM 大頁分配相關(guān)參數(shù)與機制(涉及 JVM 參數(shù):UseLargePages,UseHugeTLBFS,UseSHM,UseTransparentHugePages,LargePageSizeInBytes

    4. JVM commit 的內(nèi)存與實際占用內(nèi)存的差異

    5. Linux 下內(nèi)存管理模型簡述

    6. JVM commit 的內(nèi)存與實際占用內(nèi)存的差異

    7. 大頁分配 UseLargePages(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 3.大頁分配 UseLargePages開始)

  3. Java 堆內(nèi)存相關(guān)設計(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 4.Java 堆內(nèi)存大小的確認開始)

    1. 驗證?32-bit?壓縮指針模式

    2. 驗證?Zero based?壓縮指針模式

    3. 驗證?Non-zero disjoint?壓縮指針模式

    4. 驗證?Non-zero based?壓縮指針模式

    5. 壓縮對象指針存在的意義(涉及 JVM 參數(shù):ObjectAlignmentInBytes

    6. 壓縮對象指針與壓縮類指針的關(guān)系演進(涉及 JVM 參數(shù):UseCompressedOops,UseCompressedClassPointers

    7. 壓縮對象指針的不同模式與尋址優(yōu)化機制(涉及 JVM 參數(shù):ObjectAlignmentInBytes,HeapBaseMinAddress

    8. 通用初始化與擴展流程

    9. 直接指定三個指標的方式(涉及 JVM 參數(shù):MaxHeapSize,MinHeapSize,InitialHeapSize,Xmx,Xms

    10. 不手動指定三個指標的情況下,這三個指標(MinHeapSize,MaxHeapSize,InitialHeapSize)是如何計算的

    11. 壓縮對象指針相關(guān)機制(涉及 JVM 參數(shù):UseCompressedOops)(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 5.壓縮對象指針相關(guān)機制開始)

    12. 為何預留第 0 頁,壓縮對象指針 null 判斷擦除的實現(xiàn)(涉及 JVM 參數(shù):HeapBaseMinAddress

    13. 結(jié)合壓縮對象指針與前面提到的堆內(nèi)存限制的初始化的關(guān)系(涉及 JVM 參數(shù):HeapBaseMinAddress,ObjectAlignmentInBytes,MinHeapSize,MaxHeapSize,InitialHeapSize

    14. 使用 jol + jhsdb + JVM 日志查看壓縮對象指針與 Java 堆驗證我們前面的結(jié)論

    15. 堆大小的動態(tài)伸縮(涉及 JVM 參數(shù):MinHeapFreeRatio,MaxHeapFreeRatio,MinHeapDeltaBytes)(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 6.其他 Java 堆內(nèi)存相關(guān)的特殊機制開始)

    16. 適用于長期運行并且盡量將所有可用內(nèi)存被堆使用的 JVM 參數(shù) AggressiveHeap

    17. JVM 參數(shù) AlwaysPreTouch 的作用

    18. JVM 參數(shù) UseContainerSupport - JVM 如何感知到容器內(nèi)存限制

    19. JVM 參數(shù) SoftMaxHeapSize - 用于平滑遷移更耗內(nèi)存的 GC 使用

  4. JVM 元空間設計(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 7.元空間存儲的元數(shù)據(jù)開始)

    1. jcmd <pid> VM.metaspace?元空間說明

    2. 元空間相關(guān) JVM 日志

    3. 元空間 JFR 事件詳解

    4. jdk.MetaspaceSummary?元空間定時統(tǒng)計事件

    5. jdk.MetaspaceAllocationFailure?元空間分配失敗事件

    6. jdk.MetaspaceOOM?元空間 OOM 事件

    7. jdk.MetaspaceGCThreshold?元空間 GC 閾值變化事件

    8. jdk.MetaspaceChunkFreeListSummary?元空間 Chunk FreeList 統(tǒng)計事件

    9. CommitLimiter?的限制元空間可以 commit 的內(nèi)存大小以及限制元空間占用達到多少就開始嘗試 GC

    10. 每次 GC 之后,也會嘗試重新計算?_capacity_until_GC

    11. 首先類加載器 1 需要分配 1023 字節(jié)大小的內(nèi)存,屬于類空間

    12. 然后類加載器 1 還需要分配 1023 字節(jié)大小的內(nèi)存,屬于類空間

    13. 然后類加載器 1 需要分配 264 KB 大小的內(nèi)存,屬于類空間

    14. 然后類加載器 1 需要分配 2 MB 大小的內(nèi)存,屬于類空間

    15. 然后類加載器 1 需要分配 128KB 大小的內(nèi)存,屬于類空間

    16. 新來一個類加載器 2,需要分配 1023 Bytes 大小的內(nèi)存,屬于類空間

    17. 然后類加載器 1 被 GC 回收掉

    18. 然后類加載器 2 需要分配 1 MB 大小的內(nèi)存,屬于類空間

    19. 元空間的整體配置以及相關(guān)參數(shù)(涉及 JVM 參數(shù):MetaspaceSize,MaxMetaspaceSize,MinMetaspaceExpansion,MaxMetaspaceExpansion,MaxMetaspaceFreeRatio,MinMetaspaceFreeRatio,UseCompressedClassPointers,CompressedClassSpaceSize,CompressedClassSpaceBaseAddress,MetaspaceReclaimPolicy

    20. 元空間上下文?MetaspaceContext

    21. 虛擬內(nèi)存空間節(jié)點列表?VirtualSpaceList

    22. 虛擬內(nèi)存空間節(jié)點?VirtualSpaceNode?與?CompressedClassSpaceSize

    23. MetaChunk

    24. 類加載的入口?SystemDictionary?與保留所有?ClassLoaderData?的?ClassLoaderDataGraph

    25. 每個類加載器私有的?ClassLoaderData?以及?ClassLoaderMetaspace

    26. 管理正在使用的?MetaChunk?的?MetaspaceArena

    27. 元空間內(nèi)存分配流程(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 9.元空間內(nèi)存分配流程開始)

    28. ClassLoaderData?回收

    29. ChunkHeaderPool?池化?MetaChunk?對象

    30. ChunkManager?管理空閑的?MetaChunk

    31. 類加載器到?MetaSpaceArena?的流程

    32. 從?MetaChunkArena?普通分配 - 整體流程

    33. 從?MetaChunkArena?普通分配 -?FreeBlocks?回收老的?current chunk?與用于后續(xù)分配的流程

    34. 從?MetaChunkArena?普通分配 - 嘗試從?FreeBlocks?分配

    35. 從?MetaChunkArena?普通分配 - 嘗試擴容?current chunk

    36. 從?MetaChunkArena?普通分配 - 從?ChunkManager?分配新的?MetaChunk

    37. 從?MetaChunkArena?普通分配 - 從?ChunkManager?分配新的?MetaChunk?- 從?VirtualSpaceList?申請新的?RootMetaChunk

    38. 從?MetaChunkArena?普通分配 - 從?ChunkManager?分配新的?MetaChunk?- 將?RootMetaChunk?切割成為需要的?MetaChunk

    39. MetaChunk?回收 - 不同情況下,?MetaChunk?如何放入?FreeChunkListVector

    40. 什么時候用到元空間,以及釋放時機

    41. 元空間保存什么

    42. 什么是元數(shù)據(jù),為什么需要元數(shù)據(jù)

    43. 什么時候用到元空間,元空間保存什么

    44. 元空間的核心概念與設計(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 8.元空間的核心概念與設計開始)

    45. 元空間分配與回收流程舉例(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 10.元空間分配與回收流程舉例開始)

    46. 元空間大小限制與動態(tài)伸縮(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 11.元空間分配與回收流程舉例開始)

    47. jcmd VM.metaspace?元空間說明、元空間相關(guān) JVM 日志以及元空間 JFR 事件詳解(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 12.元空間各種監(jiān)控手段開始)

  5. JVM 線程內(nèi)存設計(重點研究 Java 線程)(全網(wǎng)最硬核 JVM 內(nèi)存解析 - 13.JVM 線程內(nèi)存設計開始)

    1. 解釋執(zhí)行與編譯執(zhí)行時候的判斷(x86為例)

    2. 一個 Java 線程 Xss 最小能指定多大

    3. JVM 中有哪幾種線程,對應線程棧相關(guān)的參數(shù)是什么(涉及 JVM 參數(shù):ThreadStackSize,VMThreadStackSize,CompilerThreadStackSize,StackYellowPages,StackRedPages,StackShadowPages,StackReservedPages,RestrictReservedStack

    4. Java 線程棧內(nèi)存的結(jié)構(gòu)

    5. Java 線程如何拋出的 StackOverflowError

4. JVM 元空間設計

4.6.?jcmd VM.metaspace?元空間說明、元空間相關(guān) JVM 日志以及元空間 JFR 事件詳解

4.6.1.?jcmd <pid> VM.metaspace?元空間說明

通過?jcmd <pid> VM.metaspace?命令可以查看對應 JVM 進程的元空間當前的詳細使用情況,返回內(nèi)容是:

1.元空間從?MetaChunk?角度的使用統(tǒng)計信息

Total Usage - 1383 loaders, 33006 classes (1361 shared): ?Non-Class: 7964 chunks, ? ?150.83 MB capacity, ?150.77 MB (>99%) committed, ? 150.21 MB (>99%) used, ? 562.77 KB ( <1%) free, ? ? 6.65 KB ( <1%) waste , deallocated: 869 blocks with 249.52 KB ?Class: 2546 chunks, ? ? 21.00 MB capacity, ? 20.93 MB (>99%) committed, ? ?20.21 MB ( 96%) used, ? 741.42 KB ( ?3%) free, ? 216 bytes ( <1%) waste , deallocated: 1057 blocks with 264.88 KB ?Both: 10510 chunks, ? 171.83 MB capacity, ?171.70 MB (>99%) committed, ? 170.42 MB (>99%) used, ? ? 1.27 MB ( <1%) free, ? ? 6.86 KB ( <1%) waste , deallocated: 1926 blocks with 514.41 KB

意思是:

  1. 一共 1383 個類加載器,加載了 33006 個類(其中 1361 個是共享類)。

  2. capacity 是指?MetaChunk?的總?cè)萘看笮。?code>Reserved?內(nèi)存);committed 是指這些?MetaChunk?中?committed?的內(nèi)存大小,也就是實際占用系統(tǒng)物理內(nèi)存是這么大(雖然可能會有點細微差異,參考本篇文章的第二章);used 是指這些?MetaChunk?實際使用的大小,肯定比?committed?的要?。籪ree 是指剩余的大??;committed = used + free + waste;deallocated 是指回收到?FreeBlocks?的大小,屬于 free 的一部分,另一部分就是?MetaChunk?中?committed?但是還沒使用的部分;waste 是指浪費的大小(前面我們提到了什么造成的浪費,主要是搜索?FreeBlocks?的空間使用的時候,可能正好剩下 1 字節(jié),就不放回了繼續(xù)使用了)洗稿的狗也遇到不少

  3. 數(shù)據(jù)元空間使用情況:一共使用了 7964 個?MetaChunk,這些?MetaChunk?相關(guān)總?cè)萘看笮∈?150.83 MB,目前?commit?了?150.77 MB,使用了?150.21 MB,剩余?562.77 KB?可以使用,6.65 KB?的空間被浪費了。FreeBlocks?目前回收了?869?塊內(nèi)存,一共?249.52 KB。

  4. 類元空間使用情況:一共使用了 2546 個?MetaChunk,總?cè)萘看笮∈?21.00 MB,目前?commit?了?20.93 MB,使用了?20.21 MB,剩余?741.42 KB?可以使用,216 bytes?的空間被浪費了。FreeBlocks?目前回收了?1057?塊內(nèi)存,一共?264.88 KB

  5. 總的元空間使用情況(類元空間 + 數(shù)據(jù)元空間的):一共使用了 10510 個?MetaChunk,總?cè)萘看笮∈?171.83 MB,目前?commit?了?171.70 MB,使用了?170.42 MB,剩余?1.27 MB?可以使用,6.86 KB?的空間被浪費了。FreeBlocks?目前回收了?1926?塊內(nèi)存,一共?514.41 KB。

前面的是從?MetaChunk?的角度去查看,另一個角度是從?VirtualSpaceList?去查看,接下來的信息就是:

Virtual space: ?Non-class space: ? ? ?152.00 MB reserved, ? ? 150.81 MB (>99%) committed, ?19 nodes. ? ? ?Class space: ? ? ? ?1.00 GB reserved, ? ? ?20.94 MB ( ?2%) committed, ?1 nodes. ? ? ? ? ? ? Both: ? ? ? ?1.15 GB reserved, ? ? 171.75 MB ( 15%) committed.

意思是:

  1. 數(shù)據(jù)元空間的?VirtualSpaceList:總共?Reserve?了?152.00 MB,目前?Commit?了?150.81 MB,一共有?19?個?VirtualSpaceNode。這個與?MetaChunk?的統(tǒng)計信息是有差異的,VirtualSpaceList?的統(tǒng)計信息更體現(xiàn)元空間實際占用的,從?MetaChunk?角度統(tǒng)計的時候,將每個?MetaChunk?統(tǒng)計信息相加,會有精度損失。

  2. 類元空間的?VirtualSpaceList:總共?Reserve?了?1.00 GB,目前?Commit?了?20.94 MB,一共有?1?個?VirtualSpaceNode。

  3. 總的元空間的?VirtualSpaceList:總共?Reserve?了?1.15 GB,目前?Commit?了?171.75 MB。不要偷取他人的勞動成果,也不要浪費自己的時間和精力,讓我們一起做一個有良知的寫作者。

接下來是每個?ChunkManager?的?FreeChunkListVector?的統(tǒng)計信息:

Chunk freelists: ? Non-Class: ?4m: (none) ?2m: (none) ?1m: ? ?2, capacity=2.00 MB, committed=0 bytes ( ?0%) 512k: (none) 256k: (none) 128k: ? ?2, capacity=256.00 KB, committed=0 bytes ( ?0%) 64k: (none) 32k: ? ?2, capacity=64.00 KB, committed=0 bytes ( ?0%) 16k: (none) ?8k: ? ?2, capacity=16.00 KB, committed=0 bytes ( ?0%) ?4k: ? ?2, capacity=8.00 KB, committed=0 bytes ( ?0%) ?2k: (none) ?1k: ? ?2, capacity=2.00 KB, committed=0 bytes ( ?0%) Total word size: 2.34 MB, committed: 0 bytes ( ?0%) ? ? ? Class: ?4m: (none) ?2m: ? ?1, capacity=2.00 MB, committed=0 bytes ( ?0%) ?1m: ? ?1, capacity=1.00 MB, committed=0 bytes ( ?0%) 512k: (none) 256k: (none) 128k: (none) 64k: (none) 32k: (none) 16k: (none) ?8k: (none) ?4k: ? ?1, capacity=4.00 KB, committed=0 bytes ( ?0%) ?2k: (none) ?1k: (none) Total word size: 3.00 MB, committed: 0 bytes ( ?0%) ? ? ? ?Both: ?4m: (none) ?2m: ? ?1, capacity=2.00 MB, committed=0 bytes ( ?0%) ?1m: ? ?3, capacity=3.00 MB, committed=0 bytes ( ?0%) 512k: (none) 256k: (none) 128k: ? ?2, capacity=256.00 KB, committed=0 bytes ( ?0%) 64k: (none) 32k: ? ?2, capacity=64.00 KB, committed=0 bytes ( ?0%) 16k: (none) ?8k: ? ?2, capacity=16.00 KB, committed=0 bytes ( ?0%) ?4k: ? ?3, capacity=12.00 KB, committed=0 bytes ( ?0%) ?2k: (none) ?1k: ? ?2, capacity=2.00 KB, committed=0 bytes ( ?0%) Total word size: 5.34 MB, committed: 0 bytes ( ?0%)

以上的信息可能用圖片更直接一些:

接下來是關(guān)于回收利用的從?MetaChunk?的角度去查看一些統(tǒng)計信息:

Waste (unused committed space):(percentages refer to total committed size 171.75 MB): ? ? ? ?Waste in chunks in use: ? ? ?6.86 KB ( <1%) ? ? ? ?Free in chunks in use: ? ? ?1.27 MB ( <1%) ? ? ? ? ? ? ? ?In free chunks: ? ? ?0 bytes ( ?0%) Deallocated from chunks in use: ? ?514.41 KB ( <1%) (1926 blocks) ? ? ? ? ? ? ? ? ? ? ? -total-: ? ? ?1.78 MB ( ?1%) chunk header pool: 10520 items, 748.30 KB.

包含的信息是:

  1. 當前被使用的?MetaChunk(即存在于每個類加載器對應的?MetaspaceArena?中的?MetaChunk)中有?6.86 KB?的空間被浪費了。當前被使用的?MetaChunk(即存在于每個類加載器對應的?MetaspaceArena?中的?MetaChunk)中剩余?1.27 MB?可以使用。在?FreeChunkListVector?中沒有浪費的空間,其實從前面的?FreeChunkListVector?的詳細信息就能看出來。

  2. FreeBlocks?目前回收了?1926?塊內(nèi)存,一共?514.41 KB。FreeBlocks?里面有?1926?個?FreeBlock,一共?514.41 KB

  3. ChunkHeaderPool?目前有?10520?個?ChunkHeader,一共占用?748.30 KB

然后是一些統(tǒng)計信息:

Internal statistics: num_allocs_failed_limit: 24. num_arena_births: 2768. num_arena_deaths: 2. num_vsnodes_births: 20. num_vsnodes_deaths: 0. num_space_committed: 2746. num_space_uncommitted: 0. num_chunks_returned_to_freelist: 28. num_chunks_taken_from_freelist: 10515. num_chunk_merges: 9. num_chunk_splits: 6610. num_chunks_enlarged: 4139. num_purges: 2. num_inconsistent_stats: 0.

包含的信息是:

  1. num_allocs_failed_limit:元空間普通分批內(nèi)存失敗的次數(shù)(前文分析過詳細流程),后面也有對應的 JFR 事件會分析。

  2. num_arena_birthsMetaspaceArena?的創(chuàng)建次數(shù)。

  3. num_arena_deathsMetaspaceArena?的銷毀次數(shù)。發(fā)生于對應的類加載器被回收之后。

  4. num_vsnodes_birthsVirtualSpaceNode?的創(chuàng)建次數(shù)。(根據(jù)前面的?VirtualSpaceList?的統(tǒng)計信息可以知道是 19 + 1 = 20)

  5. num_vsnodes_deathsVirtualSpaceNode?的銷毀次數(shù)。

  6. num_space_committedCommit?內(nèi)存的次數(shù)。

  7. num_space_uncommittedUncommit?內(nèi)存的次數(shù)。

  8. num_chunks_returned_to_freelistMetaChunk?被回收到?FreeChunkListVector?的次數(shù)。

  9. num_chunks_taken_from_freelist:從?FreeChunkListVector?中獲取?MetaChunk?進行分配的次數(shù)。

  10. num_chunk_mergesMetaChunk?合并的次數(shù)。

  11. num_chunk_splitsMetaChunk?拆分的次數(shù)。

  12. num_chunks_enlargedMetaChunk?擴容的次數(shù)。

  13. num_purgesMetaspaceArena?的清理次數(shù)。一般等于銷毀次數(shù)。

  14. num_inconsistent_stats:不一致的統(tǒng)計次數(shù)。這個一般不用關(guān)心,主要是為了調(diào)試用的。

最后是一些參數(shù)信息:

Settings: MaxMetaspaceSize: unlimited CompressedClassSpaceSize: 1.00 GB Initial GC threshold: 40.00 MB Current GC threshold: 210.12 MB CDS: on MetaspaceReclaimPolicy: balanced - commit_granule_bytes: 65536. - commit_granule_words: 8192. - virtual_space_node_default_size: 1048576. - enlarge_chunks_in_place: 1. - new_chunks_are_fully_committed: 0. - uncommit_free_chunks: 1. - use_allocation_guard: 0. - handle_deallocations: 1.

  1. MaxMetaspaceSize:元空間最大值。默認是無限制的。這里我們也沒限制。

  2. CompressedClassSpaceSize:壓縮類空間大小。默認是 1 GB。這里我們也沒指定,所以是默認的。

  3. Initial GC threshold:初始的元空間 GC 閾值。默認是 40 MB。這里我們也沒指定,所以是默認的。

  4. Current GC threshold:當前的元空間 GC 閾值。前面我們分析過這個閾值改變的機制。

  5. CDS:是否開啟了 CDS。默認開啟。這個我們不用太關(guān)心,主要和 CDS 特性相關(guān)(JEP 310: Application Class-Data Sharing?和?JEP 350: Dynamic CDS Archives),在以后的文章會詳細分析。

  6. 元空間?MetaspaceReclaimPolicy?為?balanced

  7. commit 粒度(commit_granule_bytes)為 65536 字節(jié),轉(zhuǎn)化單位為字之后,是 8192 字(一 word 為 8 字節(jié))。虛擬內(nèi)存空間節(jié)點內(nèi)存大?。?code>virtual_space_node_default_size)為 1048576 字,轉(zhuǎn)化單位為字之后,是 64 MB。當前 MetaChunk 不足以分配的時候,是否嘗試擴容當前?MetaChunkenlarge_chunks_in_place)為是,新分配的?MetaChunk?是否一次性全部 commit(new_chunks_are_fully_committed)為否,是否在?MetaChunk?釋放的時候 uncommit(uncommit_free_chunks)為是。以上配置都在前文分析過。最后兩個配置都是 debug 配置,正式版里面都是無法修改的,我們也不用太關(guān)心這兩個配置的效果,并且?handle_deallocations?已經(jīng)在 Java 18 中移除了(https://github.com/openjdk/jdk/commit/157e1d5073e221dab084422389f68eea53974f4c

4.6.2. 元空間相關(guān) JVM 日志

我們通過啟動參數(shù)?-Xlog:metaspace*=debug::utctime,level,tags,查看元空間相關(guān) JVM 日志。

首先,初始化 JVM 元空間的時候,會輸出元空間基本參數(shù):

[2023-04-11T09:07:31.994+0000][info][metaspace] Initialized with strategy: balanced reclaim. [2023-04-11T09:07:31.994+0000][info][metaspace] ?- commit_granule_bytes: 65536. [2023-04-11T09:07:31.994+0000][info][metaspace] ?- commit_granule_words: 8192. [2023-04-11T09:07:31.994+0000][info][metaspace] ?- virtual_space_node_default_size: 1048576. [2023-04-11T09:07:31.994+0000][info][metaspace] ?- enlarge_chunks_in_place: 1. [2023-04-11T09:07:31.994+0000][info][metaspace] ?- new_chunks_are_fully_committed: 0. [2023-04-11T09:07:31.994+0000][info][metaspace] ?- uncommit_free_chunks: 1. [2023-04-11T09:07:31.994+0000][info][metaspace] ?- use_allocation_guard: 0. [2023-04-11T09:07:31.994+0000][info][metaspace] ?- handle_deallocations: 1.

以上這幾行日志的意思是:元空間?MetaspaceReclaimPolicy?為?balanced,commit 粒度(commit_granule_bytes)為 65536 字節(jié),轉(zhuǎn)化單位為字之后,是 8192 字(一 word 為 8 字節(jié))。虛擬內(nèi)存空間節(jié)點內(nèi)存大小(virtual_space_node_default_size)為 1048576 字,轉(zhuǎn)化單位為字之后,是 64 MB。當前 MetaChunk 不足以分配的時候,是否嘗試擴容當前?MetaChunkenlarge_chunks_in_place)為是,新分配的?MetaChunk?是否一次性全部 commit(new_chunks_are_fully_committed)為否,是否在?MetaChunk?釋放的時候 uncommit(uncommit_free_chunks)為是。以上配置都在前文分析過。最后兩個配置都是 debug 配置,正式版里面都是無法修改的,我們也不用太關(guān)心這兩個配置的效果,并且?handle_deallocations?已經(jīng)在 Java 18 中移除了(https://github.com/openjdk/jdk/commit/157e1d5073e221dab084422389f68eea53974f4c

接下來,初始化元空間的內(nèi)存空間:

[2023-04-11T09:07:32.411+0000][info ][gc,metaspace] CDS archive(s) mapped at: [0x0000000800000000-0x0000000800bde000-0x0000000800bde000), size 12443648, SharedBaseAddress: 0x0000000800000000, ArchiveRelocationMode: 0. [2023-04-11T09:07:32.411+0000][info ][gc,metaspace] Compressed class space mapped at: 0x0000000800c00000-0x0000000840c00000, reserved size: 1073741824 [2023-04-11T09:07:32.411+0000][info ][gc,metaspace] Narrow klass base: 0x0000000800000000, Narrow klass shift: 0, Narrow klass range: 0x100000000 [2023-04-11T09:07:32.417+0000][debug][metaspace ? ] Arena @0x0000ffff807a1cc0 (non-class sm): : born. [2023-04-11T09:07:32.417+0000][debug][metaspace ? ] Arena @0x0000ffff807a1dd0 (class sm): : born. [2023-04-11T09:07:32.417+0000][debug][metaspace ? ] CLMS @0x0000ffff807a1c80 : born (nonclass arena: 0x0000ffff807a1cc0, class arena: 0x0000ffff807a1dd0. [2023-04-11T09:07:32.411+0000][debug][metaspace ? ] VsListNode @0x0000ffff80784ab0 base 0x0000000800c00000 : born (word_size 134217728). [2023-04-11T09:07:32.417+0000][debug][metaspace ? ] VsListNode @0x0000ffff807a27b0 base 0x0000ffff52800000 : born (word_size 1048576).

這幾行日志的意思是:

  1. CDS 元數(shù)據(jù)映射到內(nèi)存的地址范圍是?[0x0000000800000000-0x0000000800bde000-0x0000000800bde000),大小為 12443648 字節(jié),共享基地址為?0x0000000800000000ArchiveRelocationMode?為關(guān)閉。這些信息我們不用太關(guān)心,主要和 CDS 特性相關(guān)(JEP 310: Application Class-Data Sharing?和?JEP 350: Dynamic CDS Archives),在以后的文章會詳細分析。

  2. 我們這里是默認配置,所以壓縮類空間是開啟的,初始化壓縮類空間,映射到內(nèi)存的地址范圍是?[0x0000000800c00000-0x0000000840c00000),Reserved 內(nèi)存大小為 1073741824 字節(jié)(1GB),默認壓縮類空間最大大小就是 1GB。加載到壓縮類空間的類的基地址為?0x0000000800000000(),偏移量為 0,范圍為?0x100000000,這個前面也簡單分析過。

  3. Bootstrap ClassLoader?創(chuàng)建了兩個?MetaspaceArena,分別是前文分析的類元空間的?MetaspaceArena?和數(shù)據(jù)元空間的?MetaspaceArena,放入對應的?ClassLoadMetaSpace?中。不要偷取他人的勞動成果,也不要浪費自己的時間和精力,讓我們一起做一個有良知的寫作者。

  4. 初始化類元空間的還有數(shù)據(jù)元空間的?VirtualSpaceList,并分別創(chuàng)建并放入各自的第一個?VirtualSpaceNode

接下來開始加載類,從元空間申請內(nèi)存進行分配:

[2023-04-11T09:07:32.411+0000][debug][metaspace] ChkMgr @0x0000ffff807863d0 (class-space): requested chunk: pref_level: lv12, max_level: lv12, min committed size: 0. [2023-04-11T09:07:32.411+0000][debug][metaspace] VsListNode @0x0000ffff80784ab0 base 0x0000000800c00000 : new root chunk @0x0000ffff807867f0, f, base 0x0000000800c00000, level lv00. [2023-04-11T09:07:32.411+0000][debug][metaspace] ChkMgr @0x0000ffff807863d0 (class-space): allocated new root chunk. [2023-04-11T09:07:32.411+0000][debug][metaspace] ChkMgr @0x0000ffff807863d0 (class-space): splitting chunk @0x0000ffff807867f0, f, base 0x0000000800c00000, level lv00 to lv12. [2023-04-11T09:07:32.411+0000][debug][metaspace] ChkMgr @0x0000ffff807863d0 (class-space): handing out chunk @0x0000ffff807867f0, u, base 0x0000000800c00000, level lv12.

這幾行日志的意思分別是:

  1. 加載類需要從元空間申請內(nèi)存,這是第一次申請,所以各個數(shù)據(jù)結(jié)構(gòu)都是空的,所以需要申請新的?MetaChunk,優(yōu)先考慮的與最大的?ChunkLevel?都是?12,對應 1KB。本次申請發(fā)生在?ChunkManager @0x0000ffff807863d0

  2. 申請新的?RootMetaChunk,基址?0x0000000800c00000

  3. 將新的?RootMetaChunk?按照之前的算法拆分到?ChunkLevel?為?12,結(jié)果是?MetaChunk @0x0000ffff807867f0,將拆出來的其他?MetaChunk?放入?ChunkManager @0x0000ffff807863d0?的?FreeListVector?中

4.6.3. 元空間 JFR 事件詳解

4.6.3.1.?jdk.MetaspaceSummary?元空間定時統(tǒng)計事件

元空間定時統(tǒng)計事件?jdk.MetaspaceSummary,包括以下屬性:

  • 事件開始時間:其實就是事件發(fā)生時間

  • GC Identifier:全局 GC 的 id 標識

  • When:事件發(fā)生的時機,包括?Before GC?和?After GC?兩種,分別是 GC 前和 GC 后的統(tǒng)計數(shù)據(jù),可以根據(jù) GC Identifier 對比 GC 前后的數(shù)據(jù),看看 GC 之后元空間的使用情況.plagiarism和洗稿是惡意抄襲他人勞動成果的行為,是對勞動價值的漠視和踐踏!

  • GC Threshold:GC 閾值,即前面提的?_capacity_until_GC

  • Class:Reserved:類元空間 Reserved 的內(nèi)存空間大小

  • Class:Committed:類元空間 Committed 的內(nèi)存空間大小

  • Class:Used:類元空間實際保存數(shù)據(jù)使用的內(nèi)存空間大小(前面的機制分析中我們會看到,Committed 的空間會比實際使用的大,主要因為類加載器回收,以及可能?MetaChunk?分配的時候 commit 所有內(nèi)存)

  • Data:Reserved:數(shù)據(jù)元空間 Reserved 的內(nèi)存空間大小

  • Data:Committed:數(shù)據(jù)元空間 Committed 的內(nèi)存空間大小

  • Data:Used:數(shù)據(jù)元空間實際保存數(shù)據(jù)使用的內(nèi)存空間大小

  • Total:Reserved:整個元空間 Reserved 的內(nèi)存空間大小(其實就是類元空間 + 數(shù)據(jù)元空間)

  • Total:Committed:整個元空間 Committed 的內(nèi)存空間大?。ㄆ鋵嵕褪穷愒臻g + 數(shù)據(jù)元空間)

  • Total:Used:整個元空間實際保存數(shù)據(jù)使用的內(nèi)存空間大?。ㄆ鋵嵕褪穷愒臻g + 數(shù)據(jù)元空間)

4.6.3.2.?jdk.MetaspaceAllocationFailure?元空間分配失敗事件

前面提到過,如果普通分配失敗,那么會觸發(fā)?jdk.MetaspaceAllocationFailure?這個 JFR 事件,大家可以監(jiān)控這個事件,去調(diào)整元空間大小減少由于元空間不足觸發(fā)的 GC,這個事件包括以下屬性:

  • 事件開始時間:其實就是事件發(fā)生時間

  • 類加載器:觸發(fā) OOM 的類加載器

  • Hidden Class Loader:是否是隱藏類加載器

  • Metadata Type:元數(shù)據(jù)類型,分為屬于類元空間的以及屬于數(shù)據(jù)元空間的兩種類型,分別是:Class?和?Metadata

  • Metaspace Object Type:元空間對象類型,包括?Class、ConstantPoolSymbol、MethodKlass、ModulePackage、Other

  • Size:本次分配的大小

這個事件也會采集堆棧信息,用來定位分配失敗的源頭是哪些類的加載導致的。

4.6.3.3.?jdk.MetaspaceOOM?元空間 OOM 事件

前面提到過,當元空間 OOM 的時候,就會產(chǎn)生這個事件,這個事件包括以下屬性(和?jdk.MetaspaceAllocationFailure?事件一樣):

  • 事件開始時間:其實就是事件發(fā)生時間

  • 類加載器:觸發(fā) OOM 的類加載器

  • Hidden Class Loader:是否是隱藏類加載器

  • Metadata Type:元數(shù)據(jù)類型,分為屬于類元空間的以及屬于數(shù)據(jù)元空間的兩種類型,分別是:Class?和?Metadata

  • Metaspace Object Type:元空間對象類型,包括?ClassConstantPool、Symbol、Method、KlassModule、Package、Other

  • Size:本次分配的大小

與?jdk.MetaspaceAllocationFailure?事件一樣,也會采集堆棧信息,用來定位 OOM 的原因。

4.6.3.4.?jdk.MetaspaceGCThreshold?元空間 GC 閾值變化事件

前面我們說過,元空間的 GC 閾值(_capacity_until_GC)是動態(tài)調(diào)整的,這個事件就是用來記錄元空間 GC 閾值變化的。這個事件包括以下屬性:

  • 事件開始時間:其實就是事件發(fā)生時間

  • New Value:新的 GC 閾值

  • Old Value:舊的 GC 閾值

  • Updater:哪個機制觸發(fā)的 GC 閾值修改,我們之前討論過?_capacity_until_GC?有兩個場景會修改:

    • 分配過程中,達到 GC 閾值,觸發(fā) GC,但是處于 GCLocker 處于鎖定禁止 GC,就嘗試增大?_capacity_until_GC?進行分配。對應的?Updater?是?expand_and_allocate

    • 每次 GC 之后,觸發(fā)重新計算?_capacity_until_GC,如果有更新,就會生成這個事件,對應的?Updater?是?compute_new_size

4.6.3.5.?jdk.MetaspaceChunkFreeListSummary?元空間 Chunk FreeList 統(tǒng)計事件

這個事件在 Java 16 引入 JEP 387: Elastic Metaspace 彈性元空間的設計之后,里面的統(tǒng)計數(shù)據(jù)就都是 0 了,還沒有實現(xiàn),參考:https://bugs.openjdk.org/browse/JDK-8251342,所以我們先不用關(guān)心。參考源碼:https://github.com/openjdk/jdk/blob/jdk-21%2B17/src/hotspot/share/memory/metaspaceUtils.hpp

// (See JDK-8251342). Implement or Consolidate. static MetaspaceChunkFreeListSummary chunk_free_list_summary(Metaspace::MetadataType mdtype) { ? ?return MetaspaceChunkFreeListSummary(0,0,0,0,0,0,0,0); }

微信搜索“干貨滿滿張哈?!标P(guān)注公眾號,加作者微信,每日一刷,輕松提升技術(shù),斬獲各種offer
我會經(jīng)常發(fā)一些很好的各種框架的官方社區(qū)的新聞視頻資料并加上個人翻譯字幕到如下地址(也包括上面的公眾號),歡迎關(guān)注:


全網(wǎng)最硬核 JVM 內(nèi)存解析 - 12.元空間各種監(jiān)控手段的評論 (共 條)

分享到微博請遵守國家法律
准格尔旗| 乌审旗| 昭平县| 枣庄市| 亳州市| 崇明县| 团风县| 伊金霍洛旗| 苗栗市| 宾川县| 沙洋县| 北宁市| 道孚县| 南昌县| 井陉县| 清镇市| 龙海市| 乌鲁木齐县| 阿坝县| 金寨县| 洞口县| 和平县| 正定县| 阳曲县| 尉氏县| 周至县| 义乌市| 那曲县| 绥化市| 云浮市| 微博| 宁都县| 云龙县| 台中市| 南城县| 武功县| 乌鲁木齐县| 华亭县| 连山| 霸州市| 赤峰市|