色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

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

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

3天內(nèi)不再提示

Java進程所使用的內(nèi)存情況介紹

openEuler ? 來源:openEuler ? 作者:openEuler ? 2022-10-12 11:51 ? 次閱讀

0.引言

我們經(jīng)常會好奇,我啟動了一個 JVM,他到底會占據(jù)多大的內(nèi)存?他的內(nèi)存都消耗在哪里?為什么 JVM 使用的內(nèi)存比我設置的 -Xmx 大這么多?我的內(nèi)存設置參數(shù)是否合理?為什么我的 JVM 內(nèi)存一直緩慢增長?為什么我的 JVM 會被 OOMKiller 等等,這都涉及到 JAVA 虛擬機對內(nèi)存的一個使用情況,不如讓我們來一探其中究竟。

1.簡介

除去大家都熟悉的可以使用 -Xms、-Xmx 等參數(shù)設置的堆(Java Heap),JVM 還有所謂的非堆內(nèi)存(Non-Heap Memory)。

可以通過一張圖來簡單看一下 Java 進程所使用的內(nèi)存情況(簡略情況):

b309c9c2-494c-11ed-a3b6-dac502259ad0.png

非堆內(nèi)存包括方法區(qū)和Java虛擬機內(nèi)部做處理或優(yōu)化所需的內(nèi)存。

方法區(qū):在所有線程之間共享,存儲每個類的結構,如運行時常量池、字段和方法數(shù)據(jù),以及方法和構造函數(shù)的代碼。方法區(qū)在邏輯上(虛擬機規(guī)范)是堆的一部分,但規(guī)范并不限定實現(xiàn)方法區(qū)的內(nèi)存位置和編譯代碼的管理策略,所以不同的 Java 虛擬機可能有不同的實現(xiàn)方式,此處我們僅討論 HotSpot。

除了方法區(qū)域外,Java 虛擬機實現(xiàn)可能需要內(nèi)存用于內(nèi)部的處理或優(yōu)化。例如,JIT編譯器需要內(nèi)存來存儲從Java虛擬機代碼轉(zhuǎn)換的本機代碼(儲存在CodeCache中),以獲得高性能。

從 OpenJDK8 起有了一個很 nice 的虛擬機內(nèi)部功能:Native Memory Tracking (NMT) 。我們可以使用 NMT 來追蹤了解 JVM 的內(nèi)存使用詳情(即上圖中的 JVM Memory 部分),幫助我們排查內(nèi)存增長與內(nèi)存泄漏相關的問題。

2.如何使用

2.1 開啟 NMT

默認情況下,NMT是處于關閉狀態(tài)的,我們可以通過設置 JVM 啟動參數(shù)來開啟:-XX:NativeMemoryTracking=[off | summary | detail]。

注意:啟用NMT會導致5% -10%的性能開銷。

NMT 使用選項如下表所示:

NMT 選項 說明
off 不跟蹤 JVM 本地內(nèi)存使用情況。如果不指定 -XX:NativeMemoryTracking 選項則默認為off。
summary 僅跟蹤 JVM 子系統(tǒng)(如:Java heap、class、code、thread等)的內(nèi)存使用情況。
detail 除了通過 JVM 子系統(tǒng)跟蹤內(nèi)存使用情況外,還可以通過單獨的 CallSite、單獨的虛擬內(nèi)存區(qū)域及其提交區(qū)域來跟蹤內(nèi)存使用情況。

我們注意到,如果想使用 NMT 觀察 JVM 的內(nèi)存使用情況,我們必須重啟 JVM 來設置 XX:NativeMemoryTracking 的相關選項,但是重啟會使得我們丟失想要查看的現(xiàn)場,只能等到問題復現(xiàn)時才能繼續(xù)觀察。

筆者試圖通過一種不用重啟 JVM 的方式來開啟 NMT ,但是很遺憾目前沒有這樣的功能。

JVM 啟動后只有被標記為 manageable 的參數(shù)才可以動態(tài)修改或者說賦值,我們可以通過 JDK management interface (com.sun.management.HotSpotDiagnosticMXBean API) 或者 jinfo -flag 命令來進行動態(tài)修改的操作,讓我們看下所有可以被修改的參數(shù)值(JDK8):

java-XX:+PrintFlagsFinal|grepmanageable

intxCMSAbortablePrecleanWaitMillis=100{manageable}
intxCMSTriggerInterval=-1{manageable}
intxCMSWaitDuration=2000{manageable}
boolHeapDumpAfterFullGC=false{manageable}
boolHeapDumpBeforeFullGC=false{manageable}
boolHeapDumpOnOutOfMemoryError=false{manageable}
ccstrHeapDumpPath={manageable}
uintxMaxHeapFreeRatio=100{manageable}
uintxMinHeapFreeRatio=0{manageable}
boolPrintClassHistogram=false{manageable}
boolPrintClassHistogramAfterFullGC=false{manageable}
boolPrintClassHistogramBeforeFullGC=false{manageable}
boolPrintConcurrentLocks=false{manageable}
boolPrintGC=false{manageable}
boolPrintGCDateStamps=false{manageable}
boolPrintGCDetails=false{manageable}
boolPrintGCID=false{manageable}
boolPrintGCTimeStamps=false{manageable}

很顯然,其中不包含 NativeMemoryTracking 。

2.2 使用 jcmd 訪問 NMT 數(shù)據(jù)

我們可以通過jcmd命令來很方便的查看 NMT 相關的數(shù)據(jù):

jcmdVM.native_memory[summary|detail|baseline|summary.diff|detail.diff|shutdown][scale=KB|MB|GB]

jcmd 操作 NMT 選項如下表所示:

jcmd NMT 選項 說明
summary 打印按類別匯總的摘要信息
detail 打印按類別匯總的內(nèi)存使用情況
打印虛擬內(nèi)存映射
打印按 call site 匯總的內(nèi)存使用情況
baseline 創(chuàng)建一個新的內(nèi)存使用狀況的快照,用以進行比較
summary.diff 根據(jù)上一個 baseline 基線打印新的 summary 對比報告
detail.diff 根據(jù)上一個 baseline 基線打印新的 detail 對比報告
shutdown 停止NMT

NMT 默認打印的報告是 KB 來進行呈現(xiàn)的,為了滿足我們不同的需求,我們可以使用scale=MB | GB來更加直觀的打印數(shù)據(jù)。

創(chuàng)建 baseline 之后使用 diff 功能可以很直觀地對比出兩次 NMT 數(shù)據(jù)之間的差距。

看到 shutdown 選項,筆者本能的一激靈,既然我們可以通過 shutdown 來關閉 NMT ,那為什么不能通過逆向 shutdown 功能來動態(tài)的開啟 NMT 呢?筆者找到 shutdown 相關源碼(以下都是基于 OpenJDK 8):

#hotspot/src/share/vm/services/nmtDCmd.cpp
voidNMTDCmd::execute(DCmdSourcesource,TRAPS){

//CheckNMTstate
//nativememorytrackinghastobeon
if(MemTracker::tracking_level()==NMT_off){
output()->print_cr("Nativememorytrackingisnotenabled");
return;
}elseif(MemTracker::tracking_level()==NMT_minimal){
output()->print_cr("Nativememorytrackinghasbeenshutdown");
return;
}

......
//執(zhí)行shutdown操作
elseif(_shutdown.value()){
MemTracker::shutdown();
output()->print_cr("Nativememorytrackinghasbeenturnedoff");
}
......

}

#hotspot/src/share/vm/services/memTracker.cpp
//ShutdowncanonlybeissuedviaJCmd,andNMTJCmdisserializedbylock
voidMemTracker::shutdown(){
//WecanonlyshutdownNMTtominimaltrackinglevelifitiseveron.
if(tracking_level()>NMT_minimal){
transition_to(NMT_minimal);
}
}

#hotspot/src/share/vm/services/nmtCommon.hpp
//Nativememorytrackinglevel//NMT的追蹤等級
enumNMT_TrackingLevel{
NMT_unknown=0xFF,
NMT_off=0x00,
NMT_minimal=0x01,
NMT_summary=0x02,
NMT_detail=0x03
};

遺憾的是通過源碼我們發(fā)現(xiàn),shutdown 操作只是將 NMT 的追蹤等級 tracking_level 變成了 NMT_minimal 狀態(tài)(而并不是直接變成了 off 狀態(tài)),注意注釋:We can only shutdown NMT to minimal tracking level if it is ever on(即我們只能將NMT關閉到最低跟蹤級別,如果它曾經(jīng)打開)。

這就導致了如果我們沒有開啟過 NMT ,那就沒辦法通過魔改 shutdown 操作逆向打開 NMT ,因為 NMT 追蹤的部分內(nèi)存只在 JVM 啟動初始化的階段進行記錄(如在初始化堆內(nèi)存分配的過程中通過 NMT_TrackingLevel level = MemTracker::tracking_level(); 來獲取 NMT 的追蹤等級,視等級來記錄內(nèi)存使用情況),JVM 啟動之后再開啟 NMT 這部分內(nèi)存的使用情況就無法記錄,所以目前來看,還是只能在重啟 JVM 后開啟 NMT。

至于提供 shutdown 功能的原因,應該就是讓用戶在開啟 NMT 功能之后如果想要關閉,不用再次重啟 JVM 進程。shutdown 會清理虛擬內(nèi)存用來追蹤的數(shù)據(jù)結構,并停止一些追蹤的操作(如記錄 malloc 內(nèi)存的分配)來降低開啟 NMT 帶來的性能耗損,并且通過源碼可以發(fā)現(xiàn) tracking_level 變成 NMT_minimal 狀態(tài)后也不會再執(zhí)行 jcmd VM.native_memory 命令相關的操作。

2.3 虛擬機退出時獲取 NMT 數(shù)據(jù)

除了在虛擬機運行時獲取 NMT 數(shù)據(jù),我們還可以通過兩個參數(shù):-XX:+UnlockDiagnosticVMOptions和-XX:+PrintNMTStatistics,來獲取虛擬機退出時內(nèi)存使用情況的數(shù)據(jù)(輸出數(shù)據(jù)的詳細程度取決于你設定的跟蹤級別,如 summary/detail 等)。

-XX:+UnlockDiagnosticVMOptions:解鎖用于診斷 JVM 的選項,默認關閉。
-XX:+PrintNMTStatistics:當啟用 NMT 時,在虛擬機退出時打印內(nèi)存使用情況,默認關閉,需要開啟前置參數(shù) -XX:+UnlockDiagnosticVMOptions 才能正常使用。

3.NMT 內(nèi)存 & OS 內(nèi)存概念差異性

我們可以做一個簡單的測試,使用如下參數(shù)啟動 JVM :

-Xmx1G-Xms1G-XX:+UseG1GC-XX:MaxMetaspaceSize=256m-XX:MaxDirectMemorySize=256m-XX:ReservedCodeCacheSize=256M-XX:NativeMemoryTracking=detail

然后使用 NMT 查看內(nèi)存使用情況(因各環(huán)境資源參數(shù)不一樣,部分未明確設置數(shù)據(jù)可能由虛擬機根據(jù)資源自行計算得出,以下數(shù)據(jù)僅供參考):

jcmdVM.native_memorydetail

NMT 會輸出如下日志:

NativeMemoryTracking:

Total:reserved=2813709KB,committed=1497485KB
-JavaHeap(reserved=1048576KB,committed=1048576KB)
(mmap:reserved=1048576KB,committed=1048576KB)

-Class(reserved=1056899KB,committed=4995KB)
(classes#442)
(malloc=131KB#259)
(mmap:reserved=1056768KB,committed=4864KB)

-Thread(reserved=258568KB,committed=258568KB)
(thread#127)
(stack:reserved=258048KB,committed=258048KB)
(malloc=390KB#711)
(arena=130KB#234)

-Code(reserved=266273KB,committed=4001KB)
(malloc=33KB#309)
(mmap:reserved=266240KB,committed=3968KB)

-GC(reserved=164403KB,committed=164403KB)
(malloc=92723KB#6540)
(mmap:reserved=71680KB,committed=71680KB)

-Compiler(reserved=152KB,committed=152KB)
(malloc=4KB#36)
(arena=148KB#21)

-Internal(reserved=14859KB,committed=14859KB)
(malloc=14827KB#3632)
(mmap:reserved=32KB,committed=32KB)

-Symbol(reserved=1423KB,committed=1423KB)
(malloc=936KB#111)
(arena=488KB#1)

-NativeMemoryTracking(reserved=330KB,committed=330KB)
(malloc=118KB#1641)
(trackingoverhead=211KB)

-ArenaChunk(reserved=178KB,committed=178KB)
(malloc=178KB)

-Unknown(reserved=2048KB,committed=0KB)
(mmap:reserved=2048KB,committed=0KB)

......

大家可能會發(fā)現(xiàn) NMT 所追蹤的內(nèi)存(即 JVM 中的 Reserved、Committed)與操作系統(tǒng) OS (此處指Linux)的內(nèi)存概念存在一定的差異性。

首先按我們理解的操作系統(tǒng)的概念:

操作系統(tǒng)對內(nèi)存的分配管理典型地分為兩個階段:保留(reserve)和提交(commit)。保留階段告知系統(tǒng)從某一地址開始到后面的dwSize大小的連續(xù)虛擬內(nèi)存需要供程序使用,進程其他分配內(nèi)存的操作不得使用這段內(nèi)存;提交階段將虛擬地址映射到對應的真實物理內(nèi)存中,這樣這塊內(nèi)存就可以正常使用[1]。

如果使用 top 或者 smem 等命令查看剛才啟動的 JVM 進程會發(fā)現(xiàn):

top

PIDUSERPRNIVIRTRESSHRS%CPU%MEMTIME+COMMAND
36257dou+20010.8g5420017668S99.70.013:04.15java

此時疑問就產(chǎn)生了,為什么 NMT 中的 committed ,即日志詳情中 Total: reserved=2813709KB, committed=1497485KB 中的 1497485KB 與 top 中 RES 的大小54200KB 存在如此大的差異?

使用 man 查看 top 中 RES 的概念(不同版本 Linux 可能不同):

RES--ResidentMemorySize(KiB)
Asubsetofthevirtualaddressspace(VIRT)representingthenon-swappedphysicalmemoryataskiscurrentlyusing.ItisalsothesumoftheRSan,
RSfdandRSshfields.

Itcanincludeprivateanonymouspages,privatepagesmappedtofiles(includingprogramimagesandsharedlibraries)plussharedanonymouspages.
AllsuchmemoryisbackedbytheswapfilerepresentedseparatelyunderSWAP.

Lastly,thisfieldmayalsoincludesharedfile-backedpageswhich,whenmodified,actasadedicatedswapfileandthuswillneverimpactSWAP.

RES 表示任務當前使用的非交換物理內(nèi)存(此時未發(fā)生swap),那按對操作系統(tǒng) commit 提交內(nèi)存的理解,這兩者貌似應該對上,為何現(xiàn)在差距那么大呢?

筆者一開始猜測是 JVM 的 uncommit 機制(如 JEP 346[2],支持 G1 在空閑時自動將 Java 堆內(nèi)存返回給操作系統(tǒng),BiSheng JDK 對此做了增強與改進[3])造成的,JVM 在 uncommit 將內(nèi)存返還給 OS 之后,NMT 沒有除去返還的內(nèi)存導致統(tǒng)計錯誤。

但是在翻閱了源碼之后發(fā)現(xiàn),G1 在 shrink 縮容的時候,通常調(diào)用鏈路如下:

G1CollectedHeap::shrink->
G1CollectedHeap::shrink_helper->
HeapRegionManager::shrink_by->
HeapRegionManager::uncommit_regions->
G1PageBasedVirtualSpace::uncommit->
G1PageBasedVirtualSpace::uncommit_internal->
os::uncommit_memory

忽略細節(jié),uncommit 會在最后調(diào)用 os::uncommit_memory ,查看 os::uncommit_memory 源碼:

boolos::uncommit_memory(char*addr,size_tbytes){
boolres;
if(MemTracker::tracking_level()>NMT_minimal){
Trackertkr=MemTracker::get_virtual_memory_uncommit_tracker();
res=pd_uncommit_memory(addr,bytes);
if(res){
tkr.record((address)addr,bytes);
}
}else{
res=pd_uncommit_memory(addr,bytes);
}
returnres;
}

可以發(fā)現(xiàn)在返還 OS 內(nèi)存之后,MemTracker 是進行了統(tǒng)計的,所以此處的誤差不是由 uncommit 機制造成的。

既然如此,那又是由什么原因造成的呢?筆者在追蹤 JVM 的內(nèi)存分配邏輯時發(fā)現(xiàn)了一些端倪,此處以Code Cache(存放 JVM 生成的 native code、JIT編譯、JNI 等都會編譯代碼到 native code,其中 JIT 生成的 native code 占用了 Code Cache 的絕大部分空間)的初始化分配為例,其大致調(diào)用鏈路為下:

InitializeJVM->
Thread::vreate_vm->
init_globals->
codeCache_init->
CodeCache::initialize->
CodeHeap::reserve->
VirtualSpace::initialize->
VirtualSpace::initialize_with_granularity->
VirtualSpace::expand_by->
os::commit_memory

查看 os::commit_memory 相關源碼:

boolos::commit_memory(char*addr,size_tsize,size_talignment_hint,
boolexecutable){
boolres=os::pd_commit_memory(addr,size,alignment_hint,executable);
if(res){
MemTracker::record_virtual_memory_commit((address)addr,size,CALLER_PC);
}
returnres;
}

我們發(fā)現(xiàn) MemTracker 在此記錄了 commit 的內(nèi)存供 NMT 用以統(tǒng)計計算,繼續(xù)查看 os::pd_commit_memory 源碼,可以發(fā)現(xiàn)其調(diào)用了 os::commit_memory_impl 函數(shù)。

查看 os::commit_memory_impl 源碼:

intos::commit_memory_impl(char*addr,size_tsize,boolexec){
intprot=exec?PROT_READ|PROT_WRITE|PROT_EXEC:PROT_READ|PROT_WRITE;
uintptr_tres=(uintptr_t)::mmap(addr,size,prot,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,-1,0);
if(res!=(uintptr_t)MAP_FAILED){
if(UseNUMAInterleaving){
numa_make_global(addr,size);
}
return0;
}

interr=errno;//saveerrnofrommmap()callabove

if(!recoverable_mmap_error(err)){
warn_fail_commit_memory(addr,size,exec,err);
vm_exit_out_of_memory(size,OOM_MMAP_ERROR,"committingreservedmemory.");
}

returnerr;
}

問題的原因就在 uintptr_t res = (uintptr_t) ::mmap(addr, size, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0); 這段代碼上。

我們發(fā)現(xiàn),此時申請內(nèi)存執(zhí)行的是 mmap 函數(shù),并且傳遞的 port 參數(shù)是 PROT_READ|PROT_WRITE|PROT_EXEC 或 PROT_READ|PROT_WRITE ,使用 man 查看 mmap ,其中相關描述為:

Theprotargumentdescribesthedesiredmemoryprotectionofthemapping(andmustnotconflictwiththeopenmodeofthefile).ItiseitherPROT_NONE
orthebitwiseORofoneormoreofthefollowingflags:

PROT_EXECPagesmaybeexecuted.

PROT_READPagesmayberead.

PROT_WRITEPagesmaybewritten.

PROT_NONEPagesmaynotbeaccessed.

由此我們可以看出,JVM 中所謂的 commit 內(nèi)存,只是將內(nèi)存 mmaped 映射為可讀可寫可執(zhí)行的狀態(tài)!而在 Linux 中,在分配內(nèi)存時又是 lazy allocation 的機制,只有在進程真正訪問時才分配真實的物理內(nèi)存。所以 NMT 中所統(tǒng)計的 committed 并不是對應的真實的物理內(nèi)存,自然與 RES 等統(tǒng)計方式無法對應起來。

所以 JVM 為我們提供了一個參數(shù) -XX:+AlwaysPreTouch,使我們可以在啟動之初就按照內(nèi)存頁粒度都訪問一遍 Heap,強制為其分配物理內(nèi)存以減少運行時再分配內(nèi)存造成的延遲(但是相應的會影響 JVM 進程初始化啟動的時間),查看相關代碼:

voidos::pretouch_memory(char*start,char*end){
for(volatilechar*p=start;p

讓我們來驗證下,開啟 -XX:+AlwaysPreTouch 前后的效果。

NMT 的 heap 地址范圍:

Virtualmemorymap:
[0x00000000c0000000-0x0000000100000000]reserved1048576KBforJavaHeapfrom
[0x0000ffff93ea36d8]ReservedHeapSpace::ReservedHeapSpace(unsignedlong,unsignedlong,bool,char*)+0xb8
[0x0000ffff93e67f68]Universe::reserve_heap(unsignedlong,unsignedlong)+0x2d0
[0x0000ffff93898f28]G1CollectedHeap::initialize()+0x188
[0x0000ffff93e68594]Universe::initialize_heap()+0x15c

[0x00000000c0000000-0x0000000100000000]committed1048576KBfrom
[0x0000ffff938bbe8c]G1PageBasedVirtualSpace::commit_internal(unsignedlong,unsignedlong)+0x14c
[0x0000ffff938bc08c]G1PageBasedVirtualSpace::commit(unsignedlong,unsignedlong)+0x11c
[0x0000ffff938bf774]G1RegionsLargerThanCommitSizeMapper::commit_regions(unsignedint,unsignedlong)+0x5c
[0x0000ffff93943f54]HeapRegionManager::commit_regions(unsignedint,unsignedlong)+0x7c

對應該地址的/proc/{pid}/smaps:

//開啟前//開啟后
c0000000-100080000rw-p0000000000:000c0000000-100080000rw-p0000000000:000
Size:1049088kB Size:1049088kB
KernelPageSize:4kBKernelPageSize: 4kB
MMUPageSize: 4kBMMUPageSize: 4kB
Rss: 792kBRss: 1049088kB
Pss:792kBPss: 1049088kB
Shared_Clean: 0kBShared_Clean: 0kB
Shared_Dirty: 0kBShared_Dirty: 0kB
Private_Clean: 0kBPrivate_Clean: 0kB
Private_Dirty: 792kBPrivate_Dirty: 1049088kB
Referenced: 792kBReferenced: 1048520kB
Anonymous: 792kBAnonymous: 1049088kB
LazyFree: 0kBLazyFree: 0kB
AnonHugePages: 0kBAnonHugePages: 0kB
ShmemPmdMapped: 0kBShmemPmdMapped: 0kB
Shared_Hugetlb:0kBShared_Hugetlb: 0kB
Private_Hugetlb:0kBPrivate_Hugetlb: 0kB
Swap:0kBSwap: 0kB
SwapPss:0kBSwapPss: 0kB
Locked:0kBLocked: 0kB
VmFlags:rdwrmrmwmeacVmFlags:rdwrmrmwmeac

對應的/proc/{pid}/status:

//開啟前//開啟后
......
VmHWM:54136kBVmHWM:1179476kB
VmRSS:54136kBVmRSS:1179476kB
......
VmSwap:0kBVmSwap:0kB
...

開啟參數(shù)后的 top:

PIDUSERPRNIVIRTRESSHRS%CPU%MEMTIME+COMMAND
85376dou+20010.8g1.1g17784S99.70.414:56.31java

觀察對比我們可以發(fā)現(xiàn),開啟 AlwaysPreTouch 參數(shù)后,NMT 統(tǒng)計的 commited 已經(jīng)與 top 中的 RES 差不多了,之所以不完全相同是因為該參數(shù)只能 Pre-touch 分配 Java heap 的物理內(nèi)存,至于其他的非 heap 的內(nèi)存,還是受到 lazy allocation 機制的影響。

同理我們可以簡單看下 JVM 的 reserve 機制:

#hotspot/src/share/vm/runtime/os.cpp
char*os::reserve_memory(size_tbytes,char*addr,size_talignment_hint,
MEMFLAGSflags){
char*result=pd_reserve_memory(bytes,addr,alignment_hint);
if(result!=NULL){
MemTracker::record_virtual_memory_reserve((address)result,bytes,CALLER_PC);
MemTracker::record_virtual_memory_type((address)result,flags);
}

returnresult;
}

#hotspot/src/os/linux/vm/os_linux.cpp
char*os::pd_reserve_memory(size_tbytes,char*requested_addr,
size_talignment_hint){
returnanon_mmap(requested_addr,bytes,(requested_addr!=NULL));
}

staticchar*anon_mmap(char*requested_addr,size_tbytes,boolfixed){
......
addr=(char*)::mmap(requested_addr,bytes,PROT_NONE,
flags,-1,0);
......
}

reserve 通過mmap(requested_addr, bytes, PROT_NONE, flags, -1, 0);來將內(nèi)存映射為 PROT_NONE,這樣其他的 mmap/malloc 等就不能調(diào)用使用,從而達到了 guard memory 或者說 guard pages 的目的。

OpenJDK 社區(qū)其實也注意到了 NMT 內(nèi)存與 OS 內(nèi)存差異性的問題,所以社區(qū)也提出了相應的 Enhancement 來增強功能:

JDK-8249666[4]:

目前 NMT 將分配的內(nèi)存顯示為 Reserved 或 Committed。而在 top 或 pmap 的輸出中,首次使用(即 touch)之前 Reserved 和 Committed 的內(nèi)存都將顯示為 Virtual memory。只有在內(nèi)存頁(通常是4k)首次寫入后,它才會消耗物理內(nèi)存,并出現(xiàn)在 top/pmap 輸出的 “常駐內(nèi)存”(即 RSS)中。

當前NMT輸出的主要問題是,它無法區(qū)分已 touch 和未 touch 的 Committed 內(nèi)存。

該 Enhancement 提出可以使用 mincore()[5]來查找 NMT 的 Committed 中 RSS 的部分,mincore() 系統(tǒng)調(diào)用讓一個進程能夠確定一塊虛擬內(nèi)存區(qū)域中的分頁是否駐留在物理內(nèi)存中。mincore()已在JDK-8191369 NMT:增強線程堆棧跟蹤中實現(xiàn),需要將其擴展到所有其他類型的內(nèi)存中(如 Java 堆)。

遺憾的是該 Enhancement 至今仍是 Unresolved 狀態(tài)。

JDK-8191369[6]:

1 中提到的 NMT:增強線程堆棧跟蹤。使用 mincore() 來追蹤駐留在物理內(nèi)存中的線程堆棧的大小,用以解決線程堆棧追蹤時有時會夸大內(nèi)存使用情況的痛點。

該 Enhancement 已經(jīng)在 JDK11 中實現(xiàn)。

由于內(nèi)容較多,關于NMT追蹤區(qū)域分析的內(nèi)容將在下篇文章進行分享,敬請期待!

審核編輯:彭靜
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 內(nèi)存
    +關注

    關注

    8

    文章

    3052

    瀏覽量

    74223
  • JAVA
    +關注

    關注

    19

    文章

    2974

    瀏覽量

    104980
  • 函數(shù)
    +關注

    關注

    3

    文章

    4345

    瀏覽量

    62880
  • 虛擬機
    +關注

    關注

    1

    文章

    931

    瀏覽量

    28361

原文標題:Native Memory Tracking 詳解(1):基礎介紹

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

收藏 人收藏

    評論

    相關推薦

    Java多線程的用法

    本文將介紹一下Java多線程的用法。 基礎介紹 什么是多線程 指的是在一個進程中同時運行多個線程,每個線程都可以獨立執(zhí)行不同的任務或操作。 與單線程相比,多線程可以提高程序的并發(fā)性和響
    的頭像 發(fā)表于 09-30 17:07 ?984次閱讀

    Linux 查看進程和刪除進程

    ps 命令用于查看當前正在運行的進程。grep 是搜索例如: ps -ef | grep java表示查看所有進程里 CMD 是 java進程
    發(fā)表于 04-24 00:04

    Linux上對進程進行內(nèi)存分析和內(nèi)存泄漏定位

    在Linux產(chǎn)品開發(fā)過程中,通常需要注意系統(tǒng)內(nèi)存使用量,和評估單一進程內(nèi)存使用情況,便于我們選取合適的機器配置,來部署我們的產(chǎn)品。Linux本身提供了一些工具方便我們達成這些需求,查
    發(fā)表于 07-09 08:15

    linux內(nèi)存進程查看

    用 'top -i' 看看有多少進程處于 Running 狀態(tài),可能系統(tǒng)存在內(nèi)存或 I/O 瓶頸,用 free 看看系統(tǒng)內(nèi)存使用情況,swap 是否被占用很多,用 iostat 看看
    發(fā)表于 07-16 06:28

    Java程序內(nèi)存低效使用問題的分析

    Java程序內(nèi)存的低效使用是導致其性能問題的主要因素。該文分析了泄漏對象、蚍蜉對象和空閑對象3類導致內(nèi)存低效使用的情況,探討解決上述問題的方法,并提出構造對象行為模式
    發(fā)表于 04-09 09:39 ?12次下載

    java線程內(nèi)存模型

    一、Java內(nèi)存模型 按照官方的說法:Java 虛擬機具有一個堆,堆是運行時數(shù)據(jù)區(qū)域,所有類實例和數(shù)組的內(nèi)存均從此處分配。 JVM主要管理兩種類型內(nèi)
    發(fā)表于 09-27 10:55 ?0次下載
    <b class='flag-5'>java</b>線程<b class='flag-5'>內(nèi)存</b>模型

    Java內(nèi)存模型及原理分析

    一、Java內(nèi)存模型 按照官方的說法:Java 虛擬機具有一個堆,堆是運行時數(shù)據(jù)區(qū)域,所有類實例和數(shù)組的內(nèi)存均從此處分配。 JVM主要管理兩種類型內(nèi)
    發(fā)表于 09-28 11:49 ?0次下載
    <b class='flag-5'>Java</b><b class='flag-5'>內(nèi)存</b>模型及原理分析

    Linux下進程內(nèi)存結構

    Linux操作系統(tǒng)采用虛擬內(nèi)存管理技術,使得每個進程都有各自互不干涉的進程地址空間。該地址空間是大小為4GB的線性虛擬空間,用戶看到和接觸到的都是該虛擬地址,無法看到實際的物理
    發(fā)表于 06-01 09:17 ?1492次閱讀
    Linux下<b class='flag-5'>進程</b>的<b class='flag-5'>內(nèi)存</b>結構

    進程虛擬內(nèi)存布局以及進程的虛擬內(nèi)存分配釋放流程,涉及的代碼

    我們計劃通過一系列文章來介紹虛擬內(nèi)存分配/釋放,缺頁處理,內(nèi)存壓縮/回收,內(nèi)存分配器等知識,梳理虛擬內(nèi)存的管理。本章節(jié)結合代碼
    的頭像 發(fā)表于 06-28 09:38 ?4196次閱讀

    java內(nèi)存溢出的幾種原因和解決辦法

    Java是一種使用垃圾回收機制的編程語言,由于自動內(nèi)存管理機制的存在,Java程序中發(fā)生內(nèi)存溢出(Out of Memory)錯誤的情況相對
    的頭像 發(fā)表于 11-23 14:44 ?6258次閱讀

    java內(nèi)存溢出排查方法

    過程中常見的問題之一,可能導致應用程序崩潰、性能下降甚至系統(tǒng)崩潰。在本文中,將詳細介紹如何排查和解決Java內(nèi)存溢出問題。 一、什么是Java內(nèi)存
    的頭像 發(fā)表于 11-23 14:46 ?3318次閱讀

    如何查看java程序的內(nèi)存分布

    。 程序計數(shù)器: 程序計數(shù)器是一塊較小的內(nèi)存空間,它的作用是指示當前線程執(zhí)行的字節(jié)碼指令的行號。在多線程環(huán)境下,每個線程都有一個獨立的程序計數(shù)器,用于記錄當前線程執(zhí)行的字節(jié)碼指令。 Java虛擬機棧:
    的頭像 發(fā)表于 11-23 14:47 ?1093次閱讀

    jmap dump內(nèi)存的命令是

    jmap dump是Java內(nèi)存映像工具(Java Memory Map Tool)的一個功能,用于生成Java虛擬機(JVM)中的堆內(nèi)存
    的頭像 發(fā)表于 12-05 10:38 ?3238次閱讀

    jvm內(nèi)存分析命令和工具

    介紹JVM內(nèi)存分析命令和工具,并詳細介紹它們的使用方法和功能。 一、JVM內(nèi)存分析命令 jps命令:jps命令用于顯示當前系統(tǒng)中正在運行的Java
    的頭像 發(fā)表于 12-05 11:07 ?1237次閱讀

    java虛擬機內(nèi)存包括遠空間內(nèi)存

    Java虛擬機(JVM)內(nèi)存Java程序執(zhí)行時使用的內(nèi)存空間的總稱,包括了Java堆、方法區(qū)
    的頭像 發(fā)表于 12-05 14:15 ?427次閱讀
    主站蜘蛛池模板: 最近中文字幕2019免费版 | 成人性生交大片免费看中文 | 综合色中色 | 中文字幕在线永久 | 青青草国产自偷拍 | 高h超辣bl文| 精品国产自在现线拍400部 | 97久久精品人人槡人妻人 | 色欲AV亚洲永久无码精品麻豆 | 99久久爱看免费观看 | 亲爱的妈妈6韩国电影免费观看 | 99国产精品白浆在线观看免费 | 白丝女仆被强扒内裤 | 一区二区三区毛AAAA片特级 | 久久精品无码人妻无码AV蜜臀 | 欧美在线亚洲综合国产人 | 扒开粉嫩的小缝末成年小美女 | 天美传媒MV高清免费看 | 久久久国产精品免费A片蜜芽广 | 国产欧美日韩亚洲第一页 | 国产精品成人不卡在线观看 | 超级乱淫片午夜电影网99 | 日韩精品卡1卡2三卡四卡乱码 | 国产成人v视频在线观看 | 俄罗斯搜索引擎Yandex推广入口 | 国产成人无码精品久久久影院 | 国产精品搬运 | 久久久欧美国产精品人妻噜噜 | 日日夜夜影院在线播放 | 国产人妻久久久精品麻豆 | ass亚洲熟妇毛耸耸pics | 91精品婷婷国产综合久久8 | 国产自啪偷啪视频在线 | 一边喂奶一边做边爱 | 耽美肉文 高h失禁 | 97伦理电影在线不卡 | 亚洲性夜夜夜色综合网 | 久久青青草视频在线观 | 最新国产三级在线不卡视频 | 日韩一级精品久久久久 | 秋霞电影网午夜鲁丝片 |