国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

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

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

3天內不再提示

使用Java層面的工具定位內存區域

倩倩 ? 來源:芋道源碼 ? 作者:芋道源碼 ? 2022-09-20 10:57 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群


為了更好地實現對項目的管理,我們將組內一個項目遷移到MDP框架(基于Spring Boot),隨后我們就發現系統會頻繁報出Swap區域使用量過高的異常。筆者被叫去幫忙查看原因,發現配置了4G堆內內存,但是實際使用的物理內存竟然高達7G,確實不正常。JVM參數配置是“-XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+AlwaysPreTouch -XX:ReservedCodeCacheSize=128m -XX:InitialCodeCacheSize=128m, -Xss512k -Xmx4g -Xms4g,-XX:+UseG1GC -XX:G1HeapRegionSize=4M”,實際使用的物理內存如下圖所示:

d7002dc8-388d-11ed-ba43-dac502259ad0.jpgtop命令顯示的內存情況

排查過程

1. 使用Java層面的工具定位內存區域(堆內內存、Code區域或者使用unsafe.allocateMemory和DirectByteBuffer申請的堆外內存)

筆者在項目中添加-XX:NativeMemoryTracking=detailJVM參數重啟項目,使用命令jcmd pid VM.native_memory detail查看到的內存分布如下:

d734cfd8-388d-11ed-ba43-dac502259ad0.jpgjcmd顯示的內存情況

發現命令顯示的committed的內存小于物理內存,因為jcmd命令顯示的內存包含堆內內存、Code區域、通過unsafe.allocateMemory和DirectByteBuffer申請的內存,但是不包含其他Native Code(C代碼)申請的堆外內存。所以猜測是使用Native Code申請內存所導致的問題。

為了防止誤判,筆者使用了pmap查看內存分布,發現大量的64M的地址;而這些地址空間不在jcmd命令所給出的地址空間里面,基本上就斷定就是這些64M的內存所導致。

d79e3856-388d-11ed-ba43-dac502259ad0.jpg

pmap顯示的內存情況

2、使用系統層面的工具定位堆外內存

因為筆者已經基本上確定是Native Code所引起,而Java層面的工具不便于排查此類問題,只能使用系統層面的工具去定位問題。

首先,使用了gperftools去定位問題

gperftools的使用方法可以參考gperftools,gperftools的監控如下:

d7cb1f56-388d-11ed-ba43-dac502259ad0.jpggperftools監控

從上圖可以看出:使用malloc申請的的內存最高到3G之后就釋放了,之后始終維持在700M-800M。筆者第一反應是:難道Native Code中沒有使用malloc申請,直接使用mmap/brk申請的?(gperftools原理就使用動態鏈接的方式替換了操作系統默認的內存分配器(glibc)。)

然后,使用strace去追蹤系統調用

因為使用gperftools沒有追蹤到這些內存,于是直接使用命令“strace -f -e”brk,mmap,munmap” -p pid”追蹤向OS申請內存請求,但是并沒有發現有可疑內存申請。strace監控如下圖所示:

d7fb8a92-388d-11ed-ba43-dac502259ad0.jpg

strace監控

接著,使用GDB去dump可疑內存

因為使用strace沒有追蹤到可疑內存申請;于是想著看看內存中的情況。就是直接使用命令gdp -pid pid進入GDB之后,然后使用命令dump memory mem.bin startAddress endAddressdump內存,其中startAddress和endAddress可以從/proc/pid/smaps中查找。然后使用strings mem.bin查看dump的內容,如下:

d814676a-388d-11ed-ba43-dac502259ad0.jpg

gperftools監控

從內容上來看,像是解壓后的JAR包信息。讀取JAR包信息應該是在項目啟動的時候,那么在項目啟動之后使用strace作用就不是很大了。所以應該在項目啟動的時候使用strace,而不是啟動完成之后。

再次,項目啟動時使用strace去追蹤系統調用

項目啟動使用strace追蹤系統調用,發現確實申請了很多64M的內存空間,截圖如下:

d8368c00-388d-11ed-ba43-dac502259ad0.jpgstrace監控

使用該mmap申請的地址空間在pmap對應如下:

d85ca2b4-388d-11ed-ba43-dac502259ad0.jpgstrace申請內容對應的pmap地址空間

最后,使用jstack去查看對應的線程

因為strace命令中已經顯示申請內存的線程ID。直接使用命令jstack pid去查看線程棧,找到對應的線程棧(注意10進制和16進制轉換)如下:

d8b179ba-388d-11ed-ba43-dac502259ad0.jpgstrace申請空間的線程棧

這里基本上就可以看出問題來了:MCC(美團統一配置中心)使用了Reflections進行掃包,底層使用了Spring Boot去加載JAR。因為解壓JAR使用Inflater類,需要用到堆外內存,然后使用Btrace去追蹤這個類,棧如下:

d8d5867a-388d-11ed-ba43-dac502259ad0.jpg

btrace追蹤棧

然后查看使用MCC的地方,發現沒有配置掃包路徑,默認是掃描所有的包。于是修改代碼,配置掃包路徑,發布上線后內存問題解決。

3、為什么堆外內存沒有釋放掉呢?

雖然問題已經解決了,但是有幾個疑問:

  • 為什么使用舊的框架沒有問題?
  • 為什么堆外內存沒有釋放?
  • 為什么內存大小都是64M,JAR大小不可能這么大,而且都是一樣大?
  • 為什么gperftools最終顯示使用的的內存大小是700M左右,解壓包真的沒有使用malloc申請內存嗎?

帶著疑問,筆者直接看了一下Spring Boot Loader那一塊的源碼。發現Spring Boot對Java JDK的InflaterInputStream進行了包裝并且使用了Inflater,而Inflater本身用于解壓JAR包的需要用到堆外內存。而包裝之后的類ZipInflaterInputStream沒有釋放Inflater持有的堆外內存。于是筆者以為找到了原因,立馬向Spring Boot社區反饋了這個bug。但是反饋之后,筆者就發現Inflater這個對象本身實現了finalize方法,在這個方法中有調用釋放堆外內存的邏輯。也就是說Spring Boot依賴于GC釋放堆外內存。

筆者使用jmap查看堆內對象時,發現已經基本上沒有Inflater這個對象了。于是就懷疑GC的時候,沒有調用finalize。帶著這樣的懷疑,筆者把Inflater進行包裝在Spring Boot Loader里面替換成自己包裝的Inflater,在finalize進行打點監控,結果finalize方法確實被調用了。于是筆者又去看了Inflater對應的C代碼,發現初始化的使用了malloc申請內存,end的時候也調用了free去釋放內存。

此刻,筆者只能懷疑free的時候沒有真正釋放內存,便把Spring Boot包裝的InflaterInputStream替換成Java JDK自帶的,發現替換之后,內存問題也得以解決了。

這時,再返過來看gperftools的內存分布情況,發現使用Spring Boot時,內存使用一直在增加,突然某個點內存使用下降了好多(使用量直接由3G降為700M左右)。這個點應該就是GC引起的,內存應該釋放了,但是在操作系統層面并沒有看到內存變化,那是不是沒有釋放到操作系統,被內存分配器持有了呢?

繼續探究,發現系統默認的內存分配器(glibc 2.12版本)和使用gperftools內存地址分布差別很明顯,2.5G地址使用smaps發現它是屬于Native Stack。內存地址分布如下:

d94771f4-388d-11ed-ba43-dac502259ad0.jpggperftools顯示的內存地址分布

到此,基本上可以確定是內存分配器在搗鬼;搜索了一下glibc 64M,發現glibc從2.11開始對每個線程引入內存池(64位機器大小就是64M內存),原文如下:

daa424d4-388d-11ed-ba43-dac502259ad0.jpgglib內存池說明

按照文中所說去修改MALLOC_ARENA_MAX環境變量,發現沒什么效果。查看tcmalloc(gperftools使用的內存分配器)也使用了內存池方式。

為了驗證是內存池搞的鬼,筆者就簡單寫個不帶內存池的內存分配器。使用命令gcc zjbmalloc.c -fPIC -shared -o zjbmalloc.so生成動態庫,然后使用export LD_PRELOAD=zjbmalloc.so替換掉glibc的內存分配器。其中代碼Demo如下:

#include
#include
#include
#include
//作者使用的64位機器,sizeof(size_t)也就是sizeof(long)
void*malloc(size_tsize)
{
long*ptr=mmap(0,size+sizeof(long),PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,0,0);
if(ptr==MAP_FAILED){
returnNULL;
}
*ptr=size;//First8bytescontainlength.
return(void*)(&ptr[1]);//Memorythatisafterlengthvariable
}

void*calloc(size_tn,size_tsize){
void*ptr=malloc(n*size);
if(ptr==NULL){
returnNULL;
}
memset(ptr,0,n*size);
returnptr;
}
void*realloc(void*ptr,size_tsize)
{
if(size==0){
free(ptr);
returnNULL;
}
if(ptr==NULL){
returnmalloc(size);
}
long*plen=(long*)ptr;
plen--;//Reachtopofmemory
longlen=*plen;
if(size<=?len)?{
returnptr;
}
void*rptr=malloc(size);
if(rptr==NULL){
free(ptr);
returnNULL;
}
rptr=memcpy(rptr,ptr,len);
free(ptr);
returnrptr;
}

voidfree(void*ptr)
{
if(ptr==NULL){
return;
}
long*plen=(long*)ptr;
plen--;//Reachtopofmemory
longlen=*plen;//Readlength
munmap((void*)plen,len+sizeof(long));
}

通過在自定義分配器當中埋點可以發現其實程序啟動之后應用實際申請的堆外內存始終在700M-800M之間,gperftools監控顯示內存使用量也是在700M-800M左右。但是從操作系統角度來看進程占用的內存差別很大(這里只是監控堆外內存)。

筆者做了一下測試,使用不同分配器進行不同程度的掃包,占用的內存如下:

dad088ee-388d-11ed-ba43-dac502259ad0.jpg內存測試對比

為什么自定義的malloc申請800M,最終占用的物理內存在1.7G呢?

因為自定義內存分配器采用的是mmap分配內存,mmap分配內存按需向上取整到整數個頁,所以存在著巨大的空間浪費。通過監控發現最終申請的頁面數目在536k個左右,那實際上向系統申請的內存等于512k * 4k(pagesize) = 2G。為什么這個數據大于1.7G呢?

因為操作系統采取的是延遲分配的方式,通過mmap向系統申請內存的時候,系統僅僅返回內存地址并沒有分配真實的物理內存。只有在真正使用的時候,系統產生一個缺頁中斷,然后再分配實際的物理Page。

基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

總結

daec1d2a-388d-11ed-ba43-dac502259ad0.jpg流程圖

整個內存分配的流程如上圖所示。MCC掃包的默認配置是掃描所有的JAR包。在掃描包的時候,Spring Boot不會主動去釋放堆外內存,導致在掃描階段,堆外內存占用量一直持續飆升。當發生GC的時候,Spring Boot依賴于finalize機制去釋放了堆外內存;但是glibc為了性能考慮,并沒有真正把內存歸返到操作系統,而是留下來放入內存池了,導致應用層以為發生了“內存泄漏”。所以修改MCC的配置路徑為特定的JAR包,問題解決。筆者在發表這篇文章時,發現Spring Boot的最新版本(2.0.5.RELEASE)已經做了修改,在ZipInflaterInputStream主動釋放了堆外內存不再依賴GC;所以Spring Boot升級到最新版本,這個問題也可以得到解決。


審核編輯 :李倩


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

    關注

    20

    文章

    3001

    瀏覽量

    116422
  • 追蹤系統
    +關注

    關注

    0

    文章

    34

    瀏覽量

    9533

原文標題:唉,一次堆外內存泄露讓整個團隊通宵處理到爆肝!

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

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    井下作業 “定海神針”:MEMS陀螺工具定向短節的核心優勢

    在石油鉆井、采礦等井下作業中,定向精度與設備可靠性直接決定工程成敗。傳統磁羅盤易受地層磁性干擾,而 ER-Gyro-15 系列陀螺工具定向短節憑借慣性測量技術,成為突破環境限制的 “導航利器”。它以
    的頭像 發表于 03-03 14:19 ?122次閱讀

    芯片失效故障定位技術中的EMMI和OBIRCH是什么?

    ;以及非發光失效定位如金屬連線電阻異常增大、半導體材料電阻率不均勻等。針對金屬化系統層面的金屬線路橋接、空洞、材料結構異常阻值、漏電路徑等,OBIRCH具有較好檢測效果。 EMMI與OBIRCH的核心區
    發表于 02-27 14:59

    工況定選型,價值最大化:解鎖兩款陀螺工具定向短節核心價值

    在石油鉆井、礦山勘探、井下測量等嚴苛場景中,ER-Gyro-19與ER-Gyro-15作為兩款高性能MEMS陀螺工具定向短節,不受磁干擾影響。兩者均采用捷聯慣性測量技術,但二者在設計定位、結構硬件
    的頭像 發表于 02-14 15:07 ?52次閱讀
    工況定選型,價值最大化:解鎖兩款陀螺<b class='flag-5'>工具定</b>向短節核心價值

    突破磁限:MEMS 陀螺工具定向短節開啟鉆井測量新范式

    油氣鉆井中,磁干擾常導致傳統測量工具失效,制約復雜井作業與產能提升。ER-Gyro-19 MEMS陀螺工具定向短節,以無磁精準技術打破局限,成為解決這一行業痛點、賦能高效鉆井的核心裝備。 一、技術
    的頭像 發表于 02-10 14:59 ?896次閱讀

    醫院隨訪管理系統源碼,三級隨訪系統源碼,Java+Springboot,Vue,Ant-Design+MySQL5

    環境 技術架構:前后端分離 ? 開發語言:Java 開發工具:Idea , vscode ?? 前端框架:Vue,Ant-Design ?? 后端框架:Springboot ?? 數 據 庫
    的頭像 發表于 11-08 14:48 ?515次閱讀
    醫院隨訪管理系統源碼,三級隨訪系統源碼,<b class='flag-5'>Java</b>+Springboot,Vue,Ant-Design+MySQL5

    藍牙定位手環:可支持普通藍牙定位,同時支持AOA定位

    YT9智能藍牙定位手環,即可支持普通RSSI藍牙定位,同時支持藍牙AOA定位。它既能滿足日常大范圍、低精度的定位需求,也能在部署了專用基站的區域
    的頭像 發表于 10-11 17:01 ?964次閱讀
    藍牙<b class='flag-5'>定位</b>手環:可支持普通藍牙<b class='flag-5'>定位</b>,同時支持AOA<b class='flag-5'>定位</b>

    Java效率提升指南:5個Java工具選型建議及Perforce JRebel和XRebel介紹

    企業級Java環境越來越復雜,真正的破局點,可能不在“人”,而在于“工具”。5個實用建議,幫你理清Java工具的選型思路。
    的頭像 發表于 09-11 13:59 ?1483次閱讀
    <b class='flag-5'>Java</b>效率提升指南:5個<b class='flag-5'>Java</b><b class='flag-5'>工具</b>選型建議及Perforce JRebel和XRebel介紹

    harmony-utils之LocationUtil,定位相關工具

    harmony-utils之LocationUtil,定位相關工具類 harmony-utils 簡介與說明 harmony-utils 一款功能豐富且極易上手的HarmonyOS工具庫,借助眾多
    的頭像 發表于 07-03 18:13 ?519次閱讀

    MEMS陀螺工具定向短節全面升級,重新定義測量標準

    在石油天然氣測井、物探、定向鉆孔等領域,井下軌跡測量始終面臨三大挑戰:磁場干擾環境下的方位精度衰減,振動工況下需停鉆測量以及小井斜工況下的測量盲區。今天,ER-Gyro-15 MEMS 陀螺工具定
    的頭像 發表于 07-01 14:58 ?1462次閱讀
    MEMS陀螺<b class='flag-5'>工具定</b>向短節全面升級,重新定義測量標準

    化工園區人員定位系統選型分析

    系統:化工園區安全管理的戰略支撐 1、安全生產防護網的數字化升級 危險區域智能管控機制 工業園區內存在多個高風險作業區域,傳統的人工監管方式已難以滿足現代安全管理需求。定位系統通過構建
    的頭像 發表于 06-17 17:15 ?636次閱讀
    化工園區人員<b class='flag-5'>定位</b>系統選型分析

    新品發布:首款原位替換磁通門傳感器的MEMS陀螺工具定向短節

    可實現隨鉆測量或連續測量的ER-Gyro-19 MEMS陀螺工具定向短節,成為業內首款可原位替換磁通門傳感器的定向解決方案。
    的頭像 發表于 06-11 17:39 ?1286次閱讀
    新品發布:首款原位替換磁通門傳感器的MEMS陀螺<b class='flag-5'>工具定</b>向短節

    MEMS陀螺工具定向短節:測井領域的新型解決方案

    ER-Gyro-15 MEMS 陀螺工具定向短節的出現,有效解決了這些難題。 高精度測量與快速對準 [ER-Gyro-15]采用基于地球自轉角速度感應的陀螺定向技術,不受磁場影響,在強磁場干擾的井段,也能保證方位測量的高精度。 可實現對井斜角、工具面角及
    的頭像 發表于 05-13 17:30 ?591次閱讀

    MEMS陀螺工具定向短節全新發布:石油鉆井技術的革新利器

    在石油鉆井行業中,精準的井下定向、井眼軌跡控制、測量技術是確保作業高效、安全開展的關鍵。艾瑞科專為石油測井,定向鉆井打造可實現隨鉆測量或連續測量的MEMS陀螺工具定向短節--ER-Gyro-15正式發布。
    的頭像 發表于 05-06 10:45 ?1072次閱讀
    MEMS陀螺<b class='flag-5'>工具定</b>向短節全新發布:石油鉆井技術的革新利器

    滾珠螺桿的精度如何保持?

    滾珠螺桿通常用于需要精確定位的地方,高機械效率、低傳遞扭矩和幾乎為零的軸向游隙,使滾珠螺桿成為工具定位和飛機副翼驅動等應用中的重要設備。
    的頭像 發表于 05-05 17:59 ?566次閱讀
    滾珠螺桿的精度如何保持?

    Java開發者必備的效率工具——Perforce JRebel是什么?為什么很多Java開發者在用?

    Perforce JRebel是一款Java開發效率工具,旨在幫助java開發人員更快地編寫更好的應用程序。JRebel可即時重新加載對代碼的修改,無需重啟或重新部署應用程序,就能讓開發者即時看到代碼更改的效果,從而縮短開發、調
    的頭像 發表于 04-27 13:44 ?844次閱讀
    <b class='flag-5'>Java</b>開發者必備的效率<b class='flag-5'>工具</b>——Perforce JRebel是什么?為什么很多<b class='flag-5'>Java</b>開發者在用?