欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

macOS上使用gperftools定位Java內(nèi)存泄漏問(wèn)題及解決方案

 更新時(shí)間:2020年07月01日 11:38:18   作者:ponlanby  
這篇文章主要介紹了macOS上使用gperftools定位Java內(nèi)存泄漏問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

這幾天在排查一個(gè)堆外內(nèi)存泄漏的問(wèn)題時(shí)看到很多人都提到了gperftools這個(gè)神器,想要嘗試一下結(jié)果發(fā)現(xiàn)它對(duì)macOS的支持不太友好。而且大多數(shù)教程是針對(duì)C++的,里面的一通編譯鏈接的操作看得我個(gè)Java仔眼花繚亂的。所以我在這里整理一份mac和Java版的使用教程,免得大家再來(lái)踩坑了。

一、簡(jiǎn)介

gperftools是google提供的一套分析工具,包括堆內(nèi)存檢測(cè)heap-profiler,內(nèi)存泄漏分析工具h(yuǎn)eap-checker和CPU性能監(jiān)測(cè)工具cpu-profiler。眾所周知堆外內(nèi)存的泄漏是很難追蹤的,使用MAT等dump分析工具也只能從堆中最大或者最多的對(duì)象入手去分析發(fā)生泄漏的地方。而gperftools將malloc的調(diào)用替換為它自己的tcmalloc,從而統(tǒng)計(jì)所有內(nèi)存分配的行為,幫助我們更快的定位到發(fā)生泄漏的地方。

二、安裝

直接用homebrew安裝就可以了。

brew install gperftools

三、使用gperftools定位內(nèi)存泄漏

 1.示例程序

我們使用下面這段代碼來(lái)模擬一個(gè)Native Memory泄漏的場(chǎng)景,這段代碼使用native方法分配內(nèi)存并且默認(rèn)使用SoftReference持有其引用,因此如果有大量對(duì)象存活在堆中又沒(méi)有觸發(fā)Full GC的話就會(huì)導(dǎo)致他們持有的Native Memory一直不被釋放,最終耗盡物理機(jī)的內(nèi)存。

代碼地址

public class NativeMemoryLeakDemo {

 public static void main(String[] args) throws IOException, FontFormatException {
  while (true) {
   test();
  }
 }

 private static void test() throws IOException, FontFormatException {
  Resource resource = new ClassPathResource("font/font.ttf");
  Font rawFont = Font.createFont(Font.TRUETYPE_FONT, resource.getFile());
  Font usedFont = rawFont.deriveFont(Font.PLAIN, 30);

  BufferedImage bufferedImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
  Graphics2D g2 = bufferedImage.createGraphics();
  g2.setFont(usedFont);
  g2.drawString("hello world", 16, 35);
 }
}

我們先使用如下的VM參數(shù)運(yùn)行一段時(shí)間(Java8)

-XX:CMSInitiatingOccupancyFraction=80
-XX:CompressedClassSpaceSize=528482304
-XX:InitialHeapSize=3221225472
-XX:MaxDirectMemorySize=536870912
-XX:MaxHeapSize=3221225472
-XX:MaxMetaspaceSize=536870912
-XX:MaxNewSize=1157627904
-XX:MetaspaceSize=536870912
-XX:NewSize=1157627904
-XX:SurvivorRatio=8

圖1 進(jìn)程占用的全部?jī)?nèi)存

從圖中可以看到進(jìn)程占用的內(nèi)存遠(yuǎn)遠(yuǎn)大于我們所配的,很明顯這里發(fā)生了內(nèi)存泄漏。那么我們就來(lái)看看怎么使用gperftools提供的heap-profiler工具定位到是哪里發(fā)生的內(nèi)存泄漏。

2.使用heap_profiler定位內(nèi)存泄漏的位置

1) 使用tcmalloc替換malloc

打開bash_profile

vi ~/.bash_profile

指定tcmalloc庫(kù)的路徑并將其加入PATH中

export DYLD_INSERT_LIBRARIES=<gperftools_lib_path>/lib/libtcmalloc_and_profiler.dylib

其中<gperftools_lib_path>是gperftools在機(jī)器上的安裝位置,例如我是用homebrew安裝在/usr/local/Cellar/gperftools/2.7/下的,那我的路徑就是

export DYLD_INSERT_LIBRARIES=/usr/local/Cellar/gperftools/2.7/lib/libtcmalloc_and_profiler.dylib

保存并生效配置(需要重啟IDE)

source ~/.bash_profile

注:這里替換掉malloc并不會(huì)運(yùn)行heap-profiler,然而由于添加環(huán)境變量之后任何人都可以啟動(dòng)heap-profiler,因此Google不建議在生產(chǎn)環(huán)境配置。

2) 監(jiān)控內(nèi)存分配

在Idea里導(dǎo)入或創(chuàng)建我們的示例程序,在運(yùn)行設(shè)置里添加heap-profiler運(yùn)行的環(huán)境變量

HEAPPROFILE=<heap_output_path>

<heap_output_path>是heap文件的輸出地址。例如要將結(jié)果輸出到tmp文件夾下的memTrack文件中,就是

HEAPPROFILE=/tmp/memTrack

圖2 heap-profiler啟動(dòng)配置

運(yùn)行程序,可以在日志中看到heap-profiler開始跟蹤內(nèi)存分配,默認(rèn)的采樣速率是每分配100M。

圖3 heap-profiler日志

在/tmp目錄下也可以看到heap-profiler輸出的日志。

圖4 heap-profiler的輸出結(jié)果

3) 分析輸出

heap-profiler使用pprof將結(jié)果轉(zhuǎn)換成多種格式,這里分別介紹下txt和pdf的輸出

輸出txt

選取最后一次的采樣記錄memTrack.0026.heap,將其轉(zhuǎn)換成txt文件后輸出到~/HeapFile文件夾下

pprof $JAVA_HOME/bin/java --text /tmp/memTrack.0026.heap > ~/HeapFile/memTrack.txt

結(jié)果比較大,這里截取Java部分的輸出結(jié)果

Total: 2544.9 MB
  2541.9  99.9%  99.9%   2541.9  99.9% 0x00007fff6f5bb1bd
     0.0   0.0% 100.0%    298.4  11.7% _JavaMain
     0.0   0.0% 100.0%      0.0   0.0% _Java_com_apple_eawt_Application_nativeInitializeApplicationDelegate
     0.0   0.0% 100.0%      0.0   0.0% _Java_java_awt_image_BufferedImage_initIDs
     0.0   0.0% 100.0%      0.0   0.0% _Java_java_awt_image_ColorModel_initIDs
     0.0   0.0% 100.0%      0.0   0.0% _Java_java_awt_image_Raster_initIDs
     0.0   0.0% 100.0%      0.0   0.0% _Java_java_awt_image_SampleModel_initIDs
     0.0   0.0% 100.0%      0.0   0.0% _Java_java_io_UnixFileSystem_checkAccess
     0.0   0.0% 100.0%      0.1   0.0% _Java_java_io_UnixFileSystem_getBooleanAttributes0
     0.0   0.0% 100.0%      0.3   0.0% _Java_java_lang_ClassLoader_00024NativeLibrary_load
     0.0   0.0% 100.0%      0.1   0.0% _Java_java_lang_ClassLoader_defineClass1
     0.0   0.0% 100.0%      0.1   0.0% _Java_java_lang_ClassLoader_findBootstrapClass
     0.0   0.0% 100.0%      0.0   0.0% _Java_java_lang_Class_forName0
     0.0   0.0% 100.0%      0.2   0.0% _Java_java_lang_System_initProperties
     0.0   0.0% 100.0%      0.0   0.0% _Java_java_net_Inet6Address_init
     0.0   0.0% 100.0%      0.0   0.0% _Java_java_net_NetworkInterface_init
     0.0   0.0% 100.0%      0.0   0.0% _Java_java_net_PlainSocketImpl_initProto
     0.0   0.0% 100.0%      0.0   0.0% _Java_java_net_PlainSocketImpl_socketConnect
     0.0   0.0% 100.0%      0.9   0.0% _Java_java_util_zip_Inflater_inflateBytes
     0.0   0.0% 100.0%      0.2   0.0% _Java_java_util_zip_Inflater_init
     0.0   0.0% 100.0%      0.0   0.0% _Java_java_util_zip_ZipFile_getEntry
     0.0   0.0% 100.0%      0.4   0.0% _Java_java_util_zip_ZipFile_open
     0.0   0.0% 100.0%      0.0   0.0% _Java_sun_awt_CGraphicsEnvironment_registerDisplayReconfiguration
     0.0   0.0% 100.0%      0.5   0.0% _Java_sun_awt_image_BufImgSurfaceData_initRaster
     0.0   0.0% 100.0%      0.1   0.0% _Java_sun_font_CFontManager_loadNativeDirFonts
     0.0   0.0% 100.0%      0.0   0.0% _Java_sun_font_StrikeCache_freeIntMemory
     0.0   0.0% 100.0%      0.4   0.0% _Java_sun_font_T2KFontScaler_createScalerContextNative
     0.0   0.0% 100.0%    764.7  30.0% _Java_sun_font_T2KFontScaler_getGlyphImageNative
     0.0   0.0% 100.0%      0.0   0.0% _Java_sun_font_T2KFontScaler_initIDs
     0.0   0.0% 100.0%   1751.7  68.8% _Java_sun_font_T2KFontScaler_initNativeScaler
     0.0   0.0% 100.0%      0.0   0.0% _Java_sun_java2d_SurfaceData_initIDs
     0.0   0.0% 100.0%      0.0   0.0% _Java_sun_java2d_loops_GraphicsPrimitiveMgr_initIDs
     0.0   0.0% 100.0%      0.4   0.0% _Java_sun_java2d_opengl_CGLGraphicsConfig_getOGLCapabilities
     0.0   0.0% 100.0%      0.0   0.0% _Java_sun_java2d_opengl_OGLRenderQueue_flushBuffer

可以看到第一行是整個(gè)程序占用的總內(nèi)存,后面按照調(diào)用棧的順序記錄了每個(gè)方法的內(nèi)存使用情況(單位: MB)

  • 第一列是使用的Direct Memory
  • 第四列是進(jìn)程以及所有被它調(diào)用的方法所占用的總內(nèi)存
  • 第二列和第五列分別是第一列和第四列的內(nèi)存占進(jìn)程總內(nèi)存的百分比
  • 第三列是第二列數(shù)據(jù)的一個(gè)累加

由于gperftools是C++下的工具,可以看到在Java下無(wú)法得到完整的監(jiān)控信息。但是我們?nèi)匀豢梢酝ㄟ^(guò)第四列找到 _Java_sun_font_T2KFontScaler_initNativeScaler 這個(gè)方法占用了最多的內(nèi)存,查看代碼可以看到這個(gè)方法是被native關(guān)鍵字修飾的,說(shuō)明很可能這里分配的內(nèi)存沒(méi)有被JVM回收。去搜索一下就能查到確實(shí)是這里分配的內(nèi)存被Font2D對(duì)象持有最終造成了泄漏。

輸出pdf

pprof還支持將統(tǒng)計(jì)結(jié)果圖形化輸出到pdf,方便我們更直觀的找到占用最多內(nèi)存的地方。這里同樣用memTrack.0026.heap,將其轉(zhuǎn)換成pdf格式后輸出到~/HeapFile文件夾下

pprof $JAVA_HOME/bin/java --pdf /tmp/memTrack.0026.heap > ~/HeapFile/memTrack.pdf

之后就可以在~/HeapFile下看到生成的pdf文件了。圖片比較大,這里也只截取一部分。

圖5 內(nèi)存分配鏈路

從圖上可以看到內(nèi)存分配的調(diào)用棧被轉(zhuǎn)化為多條調(diào)用鏈路,最終都指向AllocMem進(jìn)行內(nèi)存分配,并且內(nèi)存占比高的鏈路還被貼心的加粗。

注:如果輸出pdf的時(shí)候碰到以下錯(cuò)誤,則需要安裝對(duì)應(yīng)的依賴

dot: not found 需要安裝graphviz
brew install graphviz

ps2pdf: command not found 需要安裝ghostscript
brew install ghostscript

官方文檔

總結(jié)

到此這篇關(guān)于macOS上使用gperftools定位Java內(nèi)存泄漏問(wèn)題的文章就介紹到這了,更多相關(guān)gperftools定位Java內(nèi)存泄漏內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論